我正在尝试找出最好的、干净的、安全的(对于竞争条件)解决方案来处理每年从 1 重新开始的发票计数。 所以我们有一个增量数字,每年从 1 开始。
这里是我想到的解决方案,但请添加新的解决方案并解释您使用或最喜欢哪一个,以及原因。
SELECT MAX(number) + 1
在带有 INSERT 的事务中。
对于竞赛条件来说不安全,但最常用。
触发+独特索引: 在 INSERT 之前触发,将
SELECT MAX(number) + 1
和 INSERT
发送到数据库,编号为 NULL。全部由 UNIQUE 索引包围以避免重复数字。
重复是安全的,但在竞争条件下我们会有一个例外。
边桌“柜台”。 包含字段的边表:类型、年份、last_number。 在交易中您可以执行
SELECT last_number FROM counters WHERE year = ... AND type = ... FOR UPDATE
。然后执行插入,并更新(如果为 0 则插入)计数器行。承诺解锁该行。
您可以将其与 UNIQUE 索引和 TRIGGER 结合起来以获得更多自动化。
我认为它更安全,但可能会导致死锁或大规模插入的性能问题。我认为边桌也不是那么干净。
触发+重试。 与第 2 点的解决方案相同,但代码中的重试机制(使用一段时间)会在“1062 - 无重复错误”失败时重新尝试查询。因此,插入之前触发器将在每次尝试时计算一个新数字。 也许是最安全的,但也许是脏代码。
???还有什么吗?
您使用什么以及您最喜欢哪一个?
假设还有一些其他列,我们可以从中导出基于 1 的序列,那么我选择使用
ROW_NUMBER() OVER (ORDER BY other_col)
,假设您使用的是 MySQL 8+。具体来说,您将在查询报告时生成序列列,而不是在实际插入期间。时间戳列可以具有微秒精度,这可以确保单独的发票始终具有唯一的时间戳。