无间隙序列,其中涉及多个表的多个事务

本文关键字:事务 间隙 | 更新日期: 2023-09-27 17:51:16

我有一个要求(根据法律),在不同的表上没有差距的数字。id中可以有孔,但不能有序列。

这是我必须要么解决在c#代码或在数据库(Postgres, MS SQL和Oracle)。

这是我的问题:

Start transaction 1
Start transaction 2
Insert row on table "Portfolio" in transaction 1
Get next number in sequence for column Portfolio_Sequence (1)
Insert row on table "Document" in transaction 1
Get next number in sequence for column Document_Sequence (1)
Insert row on table "Portfolio" in transaction 2
Get next number in sequence for column Portfolio_Sequence (2)
Insert row on table "Document" in transaction 2
Get next number in sequence for column Document_Sequence (2)
Problem occurred in transaction 1
Rollback transaction 1
Commit transaction 2

问题:Portfolio_SequenceDocument_Sequence的序列都有间隙。

注意,这是非常简化的,并且每个事务中包含更多的表。

我该如何处理?

我看到有人建议在事务提交或回滚之前"锁定"序列,但是当涉及这么多表和这么复杂的长事务时,这将是一个巨大的停顿。

无间隙序列,其中涉及多个表的多个事务

正如您似乎已经得出的结论,无间隙序列根本无法缩放。当回滚发生时,您可能会冒丢失值的风险,或者您的序列化点会阻止多用户并发事务系统的扩展。你不能两者兼得。

我的想法是,后处理操作怎么样?每天,您都有一个流程在营业结束时运行,检查差距,并重新编号任何需要重新编号的东西?

最后一个想法:我不知道你的要求,但是,我知道你说这是"法律要求"。那么,问问你自己,在电脑出现之前,人们在做什么?如何满足这个"需求"?假设你有一堆空白的表格,在右上角预先打印了一个"序列"号码?如果有人把咖啡洒在表格上怎么办?那是怎么处理的?看来你需要一个类似的方法来处理你的系统。

希望对你有帮助。

这个问题原则上是不可能解决的,因为任何事务都可以回滚(bug、超时、死锁、网络错误等)。

有一个串行争点。尽量减少争用:使分配数字的事务尽可能小。此外,在事务中尽可能晚地分配数字,因为只有在分配数字时才会出现争用。如果你正在做1000ms的无争用工作,然后分配一个数字(占用10ms),你仍然有100的并行度,这就足够了。

所以也许你可以用虚拟序列号插入所有行(你说有很多),只有在事务结束时,你才能快速分配所有真实序列号并更新已经写入的行。如果插入比更新多,或者更新比插入快(它们将会是这样),或者插入之间有其他处理或等待,这将工作得很好。

无间隙序列很难得到。我建议使用普通的serial列代替。使用窗口函数row_number()创建一个视图,以生成无间隙序列:

CREATE VIEW foo AS
SELECT *, row_number() OVER (ORDER BY serial_col) AS gapless_id
FROM   tbl;

这是一个既支持高性能又支持高并发性的想法:

  1. 使用一个高度并发的,缓存的Oracle序列为无间隙的表行生成一个哑唯一标识符。将这个实体命名为MASTER_TABLE

  2. 为从MASTER_TABLE到其他依赖的详细表的所有内部引用完整性使用哑唯一标识符

  3. 现在,您的无间隙MASTER_TABLE序列号可以作为MASTER_TABLE上的附加属性实现,并且将由与MASTER_TABLE行创建分离的进程填充。实际上,应该在MASTER_TABLE的第4个标准形式属性表中维护无间隙附加属性,这样单个后台线程就可以轻松地填充它,而不必担心MASTER_TABLE上的任何行锁。

  4. 所有需要在屏幕或报告上显示无间隙序列号的查询,将与无间隙附加属性第4 normal form表连接MASTER_TABLE。注意,只有在后台线程填充了无间隙附加属性第4个范式表之后,这些连接才会被满足。