postgresql生成无间隙序列

postgresql生成无间隙序列,sql,postgresql,Sql,Postgresql,我必须/必须为发票创建唯一ID。我有一个表id和另一列来表示这个唯一的数字。我使用序列化隔离级别。使用 var seq = @"SELECT invoice_serial + 1 FROM invoice WHERE ""type""=@type ORDER BY invoice_serial DESC LIMIT 1"; 没有帮助,因为即使使用FOR UPDATE,它也不会像序列化级别那样读取正确的值 唯一的解决方案似乎放了一些重试代码 您可以创建一个没有缓存的序列,然后从序列中获取下

我必须/必须为发票创建唯一ID。我有一个表id和另一列来表示这个唯一的数字。我使用序列化隔离级别。使用

  var seq = @"SELECT invoice_serial + 1 FROM  invoice WHERE ""type""=@type ORDER BY invoice_serial DESC LIMIT 1";
没有帮助,因为即使使用FOR UPDATE,它也不会像序列化级别那样读取正确的值


唯一的解决方案似乎放了一些重试代码

您可以创建一个没有缓存的序列,然后从序列中获取下一个值并将其用作计数器

CREATE SEQUENCE invoice_serial_seq START 101 CACHE 1;
SELECT nextval('invoice_serial_seq');

更多信息

您可以将表锁定为插入,和/或需要有重试代码。没有其他选择。如果您停下来思考以下情况会发生什么:

  • 并行进程回滚
  • 锁定超时

  • 您将看到原因。

    序列不会生成无间隙的数字集,而且确实没有办法让它们这样做,因为回滚或错误将“使用”序列号

    我不久前写了一篇关于这方面的文章。它是针对Oracle的,但实际上是关于无缺口数字的基本原则,我认为在这里也是如此

    嗯,又发生了。有人知道如何实现一个需求来生成一个无间隙的数字序列,一群反对者突然对他们说(这里我稍微解释一下)这会破坏系统性能,这很少是一个有效的需求,写这个需求的人都是白痴

    正如我在文章中指出的,有时生成无间隙的数字序列是真正的法律要求。英国200多万家增值税(增值税)注册机构的发票号码有这样的要求,其原因相当明显:这使得隐藏税务机关的收入来源变得更加困难。我看到有评论说这是西班牙和葡萄牙的一项要求,如果这不是许多其他国家的要求,我也不会感到惊讶

    那么,如果我们接受这是一个有效的要求,在什么情况下,无间隙数列*是一个问题?集体思考通常会让你相信它总是存在的,但事实上,它只是在非常特殊的情况下的一个潜在问题

  • 数字系列必须没有间隙
  • 多个流程创建与编号关联的实体(例如发票)
  • 必须在创建实体时生成编号
  • 如果必须满足所有这些要求,那么您的应用程序中就有一个序列化点,我们稍后将对此进行讨论

    首先,让我们讨论一下实现一系列数字需求的方法,如果您可以放弃这些需求中的任何一个

    如果您的数字序列可能有间隙(并且您有多个进程需要立即生成数字),则使用Oracle序列对象。他们的表现非常出色,可以预期存在差距的情况已经得到了很好的讨论。如果重要的话,通过设计将生成号码和提交交易之间发生流程故障的可能性降至最低,将跳过的号码数量降至最低并不太具有挑战性

    如果您没有创建实体的多个流程(并且您需要一个无间隙的编号序列,必须立即生成),比如批量生成发票,那么您已经有了序列化点。这本身可能不是问题,并且可能是执行所需操作的有效方式。在这种情况下,生成无间隙数非常简单。您可以通过多种技术读取当前最大值并对每个实体应用递增值。例如,如果要从临时工作表将一批新发票插入到发票表中,则可能:

    当然,您会保护您的流程,以便一次只能运行一个实例(如果您使用Oracle,可能会使用DBMS#u Lock),并使用唯一的密钥对发票进行保护,如果您真的非常在意,还可能使用单独的代码检查丢失的值

    如果不需要立即生成编号(但需要无间隙且多个流程生成实体),则可以允许生成实体并提交事务,然后将编号的生成留给单个批处理作业。对实体表的更新,或插入到单独的表中

    那么,如果我们需要通过多个过程即时生成一个无间隙的数字序列的三重效果呢?我们所能做的就是尽量缩短过程中的序列化时间,我提供以下建议,并欢迎任何其他建议(当然也可以是反建议)

  • 将当前值存储在专用表中。不要使用序列
  • 通过将代码封装在函数或过程中,确保所有进程使用相同的代码生成新的数字
  • 使用DBMS_锁串行访问数字生成器,确保每个系列都有自己的专用锁
  • 在序列生成器中保持锁,直到通过在提交时释放锁完成实体创建事务
  • 将数字的生成延迟到最后一个可能的时刻
  • 请考虑在生成数字之后和提交完成之前发生的意外错误的影响-应用程序是否会正常回滚并释放锁,还是会在会话稍后断开连接之前保持序列生成器上的锁?无论使用何种方法,如果事务失败,则序列号必须“返回到池”
  • 您能将整个内容封装在实体表上的触发器中吗?您能否将其封装在表或其他API调用中,以插入行并自动提交插入
    insert into
      invoices
        (
        invoice#,
        ...)
    with curr as (
      select Coalesce(Max(invoice#)) max_invoice#
      from   invoices)
    select
      curr.max_invoice#+rownum,
      ...
    from
      tmp_invoice
      ...