【问题标题】:Ensure auto_increment value ordering in MySQL确保 MySQL 中的 auto_increment 值排序
【发布时间】:2016-06-14 03:08:39
【问题描述】:

我有多个线程将事件写入 MySQL 表 events

该表有一个配置为auto_incrementtracking_no 列,用于强制对事件进行排序。 不同的读者从 events 消费,他们定期轮询表以获取新事件,并保留上次消费事件的值以在每次轮询时获取所有新事件。

事实证明,当前的实现可能会丢失一些事件。

这是正在发生的事情:

  • Thread-1 开始一个“插入”事务,它从 auto_increment 列 (1) 中获取下一个值,但需要一段时间才能完成
  • Thread-2 开始一个“插入”事务,它获取下一个 auto_incremente 值 (2) 并在 Thread-1 之前完成写入。
  • Reader 轮询并询问所有 tracking_number 大于 0 的事件;它得到事件 2,因为 Thread-1 仍然落后。 事件被消耗,Reader 将其跟踪状态更新为 2。
  • Thread-1完成插入,事件1出现在表格中。
  • Reader 再次轮询 2 之后的所有事件,当插入事件 1 时,它将永远不会被再次拾取。

这似乎可以通过更改auto_increment 策略来解决,以锁定整个表直到事务完成,但如果可能我们会避免它。

【问题讨论】:

  • 我会添加一个时间戳列,以反映事件的创建时间(即使它最后一次更新),并且消费者会在特定时间后查询插入(甚至可能更新)的事件。这将使您能够在没有表锁定的情况下使用 auto_increment。另一种解决方案是减少交易打开的时间以避免此类问题。
  • 时间戳值如何生成?

标签: mysql multithreading event-sourcing


【解决方案1】:

我能想到两种可能的方法。

1) 如果您的事件插入保证成功(即,您永远不会回滚事件插入,因此您的 tracking_no 中永远不会有任何持续的间隙),那么您可以重写您的阅读器,以便他们跟踪last contiguous event seen -- 也就是最后一个成功处理的事件。

读取器查询事件存储,开始按顺序处理事件,然后如果发现间隙则停止。剩余的事件被丢弃。下一个查询使用上次成功处理的事件的序列号。

不过,回滚会搞砸这一点 - 并发写入的场景可能会在流中留下持久的间隙,这会导致您的阅读器阻塞。

2) 您可以用时间表示的最大事件重写您的查询。有关设置时间戳列的机制,请参阅 MySQL create time and update time timestamp

然后的想法是,您的读者查询所有事件的序列号高于最后成功处理的事件,但时间戳小于 now() - 一些合理的 SLA 间隔。

事件流的投影在时间上是否稍微落后通常并不重要。因此,您可以利用这一点,读取过去的事件,从而保护您免受当前尚未完成的写入。

但这不适用于域模型——如果您正在加载事件流以准备写入,那么从过去可测量的时间间隔的流中工作不会很有趣。好消息是编写者知道他们当前正在处理对象的哪个版本,因此他们生成的事件属于序列中的哪个位置。因此,您可以跟踪架构中的版本,并将其用于冲突检测。

注意我并不完全清楚应该使用序列号进行排序。见https://stackoverflow.com/a/9985219/54734

无论如何,合成密钥 (ID) 毫无意义。它们的顺序并不重要,它们唯一重要的属性是唯一性。您无法有意义地衡量两个 ID 的“距离”有多远,也无法有意义地判断一个 ID 大于还是小于另一个。

所以这可能是一个错误的问题。

【讨论】:

  • 谢谢,第 2 点是我没有想到的。我正在评估它是否可行以及是否有其他选择。
  • 关于使用合成键进行排序:在我的场景中,我需要排序以确保事件按顺序使用,因为它们是时间敏感的。我不介意在排序(1,3,4)中有差距。我只关心按顺序处理它们。
猜你喜欢
  • 1970-01-01
  • 2016-07-26
  • 1970-01-01
  • 2021-05-07
  • 2020-03-21
  • 2013-12-22
  • 2021-03-14
  • 1970-01-01
  • 2012-01-05
相关资源
最近更新 更多