如何在 Spanner 中高效地创建强有序序列?

How can you efficiently create a strong ordered sequence in Spanner?

Google Spanner 建议不要使用时间戳或序列号之类的东西作为主键或索引的初始部分,这基于体系结构是有意义的。但是,根据我的要求,我确实需要一些方法来确保行的严格“仅附加”顺序。

我正在使用 Spanner 为事件建模(如在事件溯源中)。每个事件都会有一个类别、一个标识序列的流 ID,其中事件之间需要严格排序,以及一些有效负载字段 - 从这里开始我将忽略实际有效负载。

天真地,这将被建模为:

| Category    | STRING       |
| Stream Id   | STRING       |
| Sequence Nr | INT64        |

(使用由类别、流 ID、序列号组成的主键)这将确保一个流的事件的强排序。现在,由于某些类别有很多与之关联的事件,而 Spanner 最佳实践是在高位上有差异,因此最好将其翻转过来。每个 "stream" 将包含数量相当少的事件(几千而不是几百万),并将一起阅读以便更好地分发数据并鼓励属于一个流的事件的局部性:

| Stream Id   | STRING       |
| Category    | STRING       |
| Sequence Nr | INT64        |

但是,由于我希望能够附加事件而不必读取当前状态来找出当前序列号,所以我宁愿使用时间戳。

| Aggregate Id | STRING      |                         | 
| Category     | STRING      |                         |
| Timestamp    | TIMESTAMP   | allow_commit_timestamp  |

Spanner 有一个内置的提交时间戳,可以在实际处理事务时对其进行标记。但最后的问题是:

是否可以像上面那样表示数据并获得唯一的提交时间戳,即使我在一个事务中提交多个事件?

如果不是,是否可以通过添加额外的列来确保顺序,以其他方式确保严格排序?

documentation 指出 "Commit timestamp values are not guaranteed to be unique. Transactions that write to non-overlapping sets of fields might have the same timestamp. Transactions that write to overlapping sets of fields have unique timestamps." - 但我不太清楚在这种情况下什么构成 "sets of fields"。

文档还指出 "The commit timestamp makes creating the changelog easier, because the timestamps can enforce ordering of the changelog entries." 但不清楚在多个并发编写器或同时写入多个事件的上下文中具有强制顺序的提交时间戳的保证是什么。

如果您在同一 事务 中有多个事件,那么它们都将具有相同的提交时间戳。

字段是一个 table 单元格(一行中有一个 col 值)。所以 'non-overlapping sets of fields' 在这种情况下基本上意味着单独的行,因为字段之一 提交时间戳!

两个独立的事务,一个更新行'R1'和一个更新行'R2'在同一个table上理论上可以有相同的提交时间戳,因为它们不重叠。

Is it possible to represent data as above and get unique commit timestamps even if I commit multiple events in one transaction?

在您给出的示例中,您在主键中使用提交时间戳,那么不,您将无法在单个事务中将多个事件添加到同一 stream_id/category 对,因为它们会具有相同的时间戳 - 因此具有相同的主键。

If not, is it possible to ensure strict ordering some other way, by adding additional columns to ensure order?

如果您将提交时间戳 的组合用于每个(stream_id、类别、时间戳)元组的 sequence_number,那么您可以保留单笔交易中的严格排序:

为同一交易中的每个(stream_id,类别)对增加一个序列号,从 0 开始。 提交时间戳将确保跨不同事务的顺序,而序列号将确保顺序 within 事务...