Database design 如何在扳手中有效地创建强有序序列?

Database design 如何在扳手中有效地创建强有序序列?,database-design,google-cloud-platform,google-cloud-spanner,Database Design,Google Cloud Platform,Google Cloud Spanner,GoogleSpaner建议不要使用时间戳或序列号作为主键或索引的初始部分,这在架构上是有意义的。然而,对于我的需求,我确实需要某种方法来确保行的严格“仅附加”顺序 我正在使用扳手对事件进行建模(如在事件源中)。每个事件都有一个类别,一个流id,标识一个序列,在该序列中,事件需要严格按照相互之间的关系排序,还有几个有效负载字段——从这里开始,我将忽略实际的有效负载 天真地说,这将被建模为: | Category | STRING | | Stream Id | STRING

GoogleSpaner建议不要使用时间戳或序列号作为主键或索引的初始部分,这在架构上是有意义的。然而,对于我的需求,我确实需要某种方法来确保行的严格“仅附加”顺序

我正在使用扳手对事件进行建模(如在事件源中)。每个事件都有一个类别,一个流id,标识一个序列,在该序列中,事件需要严格按照相互之间的关系排序,还有几个有效负载字段——从这里开始,我将忽略实际的有效负载

天真地说,这将被建模为:

| Category    | STRING       |
| Stream Id   | STRING       |
| Sequence Nr | INT64        |
(主键由Category、Stream Id、Sequence Nr组成)这将确保一个流的事件的强顺序。现在,由于一些类别有很多与之相关的事件,而扳手的最佳实践是在高位有差异,所以最好将其翻转过来。每个“流”将包含合理数量的事件(数千而非数百万),并将一起读取,以便更好地分发数据,并鼓励属于一个流的事件的位置:

| Stream Id   | STRING       |
| Category    | STRING       |
| Sequence Nr | INT64        |
但是,由于我希望能够附加事件,而不必读取当前状态来查找当前序列号,因此我宁愿使用时间戳

| Aggregate Id | STRING      |                         | 
| Category     | STRING      |                         |
| Timestamp    | TIMESTAMP   | allow_commit_timestamp  |
扳手内置了一个提交时间戳,在实际处理事务时对其进行标记。但最后一个问题是:

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

如果没有,是否可以通过添加其他列以确保顺序,以其他方式确保严格的顺序?

声明“提交时间戳值不保证是唯一的。写入非重叠字段集的事务可能具有相同的时间戳。写入重叠字段集的事务具有唯一的时间戳。”—但我不太清楚在这种情况下,什么构成“字段集”


文档还指出,“提交时间戳使创建变更日志变得更容易,因为时间戳可以强制变更日志条目的顺序。”但不清楚在多个并发写入程序或同时写入多个事件的上下文中,提交时间戳具有强制顺序的保证是什么。

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

| Aggregate Id | STRING      |                         | 
| Category     | STRING      |                         |
| Timestamp    | TIMESTAMP   | allow_commit_timestamp  |
字段是一个表格单元格(一行中有一个列值)。因此,在这个上下文中,“非重叠字段集”基本上是指单独的行,因为其中一个字段是提交时间戳

同一表上的两个独立事务(一个更新行“R1”和一个更新行“R2”)理论上可以具有相同的提交时间戳,因为它们不重叠

即使我在一个事务中提交多个事件,是否可以如上所述表示数据并获得唯一的提交时间戳

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

如果没有,是否可以通过添加其他列以确保顺序,以其他方式确保严格的顺序

如果对每个(流id、类别、时间戳)元组使用提交时间戳和序列号的组合,则可以在单个事务中保持严格的顺序:

为同一事务中的每个(流id,类别)对增加一个从0开始的序列号。
提交时间戳将确保不同事务之间的顺序,序列号将确保事务中的顺序…

谢谢,这一切都是有意义的。现在,如果我引入一个“批本地”序列号,在两个编写器试图同时添加到同一个流的不太可能的情况下会发生什么?由于时间戳和批处理序列id将是主键的一部分,它们都将尝试插入相同的元组(流id、类别、时间戳、0),因此我假设其中一个将以与手动插入重复键相同的方式失败?或者,在这种情况下,它们是否保证以不同的时间戳插入?如果使用INSERT,并且两者发生在完全相同的时间,则其中一个很可能会失败,并出现重复密钥错误,因此您需要重试事务。(如果使用INSERT或UPDATE,那么其中一个将覆盖另一个,所以不要这样做!)尽管我认为这在实践中是不可能的,因为同一个扳手节点将处理两个事务,并且一个事务将在另一个事务之前处理,但最好处理错误并重试!