我必须/必须为发票创建唯一的 ID。我有一个表 ID 和这个唯一编号的另一列。我使用序列化隔离级别。使用
var seq = @"SELECT invoice_serial + 1 FROM invoice WHERE ""type""=@type ORDER BY invoice_serial DESC LIMIT 1";
没有帮助,因为即使使用 FOR UPDATE 它也不会读取序列化级别中的正确值。
唯一的解决方案似乎是放置一些重试代码。
序列不会生成无间隙的数字集,并且实际上没有办法让它们这样做,因为回滚或错误将“使用”序列号。
我不久前写过一篇关于此的文章。它是针对 Oracle 的,但实际上是关于无间隙数字的基本原则,我认为这同样适用于这里。
好吧,又发生了。有人有如何实现生成一系列无间隙数字的要求,一群反对者突然降临到他们身上说(这里我稍微解释一下)这会降低系统性能,这很少是一个有效的要求,无论谁写的要求是白痴等等等等。
正如我在帖子中指出的那样,有时生成无间隙数字系列是真正的法律要求。英国超过 2,000,000 家注册增值税(销售税)的组织的发票号码都有这样的要求,其原因相当明显:这使得向税务机关隐瞒收入的产生变得更加困难。我看到评论说这是西班牙和葡萄牙的要求,如果其他许多国家没有要求,我也不会感到惊讶。
那么,如果我们承认这是一个有效的要求,那么在什么情况下无间隙系列*的数字会成为问题呢?群体思维常常会让你相信它总是如此,但事实上它只是在非常特殊的情况下的一个潜在问题。
- 这一系列数字必须没有间隙。
- 多个进程创建与该号码关联的实体(例如发票)。
- 这些数字必须在创建实体时生成。
如果必须满足所有这些要求,那么您的应用程序中就有一个序列化点,我们稍后将讨论它。
首先让我们讨论一下实现一系列数字要求的方法(如果您可以放弃其中任何一个要求)。
如果您的数字序列可能有间隙(并且您有多个进程需要立即生成数字),那么请使用 Oracle Sequence 对象。它们的性能非常高,并且已经对可能出现差距的情况进行了很好的讨论。如果很重要的话,通过设计努力最大限度地减少生成数字和提交事务之间流程失败的机会来最小化跳过的数字量并不是太有挑战性。
如果您没有创建实体的多个流程(并且您需要必须立即生成的无间隙数字系列),就像批量生成发票的情况一样,那么您已经有了序列化点。这本身可能不是问题,并且可能是执行所需操作的有效方式。在这种情况下,生成无间隙数字相当简单。您可以读取当前的最大值,并使用多种技术将递增值应用于每个实体。例如,如果您要将一批新发票从临时工作表插入到发票表中,您可以:
insert into
invoices
(
invoice#,
...)
with curr as (
select Coalesce(Max(invoice#)) max_invoice#
from invoices)
select
curr.max_invoice#+rownum,
...
from
tmp_invoice
...
当然,您会保护您的进程,以便一次只能运行一个实例(如果您使用的是 Oracle,则可能使用 DBMS_Lock),并使用唯一的键约束保护发票#,并且可能使用单独的代码检查缺失值,如果你真的真的很在乎。
如果您不需要即时生成数字(但需要它们无间隙并且多个进程生成实体),那么您可以允许生成实体并提交事务,然后将数字的生成留给单个批次工作。对实体表的更新,或插入到单独的表中。
那么,我们是否需要通过多个进程即时生成一系列无间隙数字的三重奏呢?我们能做的就是尽量减少连载过程中的时间,我提供以下建议,并欢迎任何额外的建议(当然也包括反建议)。
- 将当前值存储在专用表中。不要使用序列。
- 通过将其封装在函数或过程中,确保所有进程使用相同的代码来生成新数字。
- 使用 DBMS_Lock 串行化对数字生成器的访问,确保每个系列都有自己的专用锁。
- 持有系列生成器中的锁,直到通过在提交时释放锁来完成实体创建事务
- 将数字的生成延迟到最后可能的时刻。
- 考虑生成数字之后和提交完成之前意外错误的影响 - 应用程序会正常回滚并释放锁,还是会保持序列生成器上的锁直到会话稍后断开连接?无论使用哪种方法,如果交易失败,则序列号必须“返回到池中”。
- 您可以将整个事情封装在实体表上的触发器中吗?您能否将其封装在表或其他插入行并自动提交插入的 API 调用中?
来源文章 http://oraclesponge.wordpress.com/2010/09/24/generating-a-gap-free-series-of-numbers-not-always-a-problem/
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)