postgresql生成没有间隙的序列

时间:2013-09-25 12:05:34

标签: sql postgresql

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

  var seq = @"SELECT invoice_serial + 1 FROM  invoice WHERE ""type""=@type ORDER BY invoice_serial DESC LIMIT 1";

没有帮助,因为即使使用FOR UPDATE它也不会像序列化级别那样读取正确的值。

只有解决方案似乎会提供一些重试代码。

4 个答案:

答案 0 :(得分:20)

序列不会产生无间隙的数字集,并且实际上没有办法让它们这样做,因为回滚或错误会使用"序号。

我刚刚写了一篇关于此的文章。它是针对Oracle的,但实际上是关于无差距数字的基本原则,我认为这同样适用于此。

  
    

嗯,它再次发生了。有人asked如何实现生成一系列无间隙数字的要求,并且一群nay-sayers已经降临到他们身上(我在这里稍微解释一下)这会杀死系统性能,就是这样很少有效的要求,无论谁写了这个要求都是白痴,等等等等。

         

正如我在线索中指出的那样,有时候产生无间隙数字系列是真正的法律要求。已注册增值税(销售税)的英国2,000,000多个组织的发票编号有这样的要求,其原因很明显:这使得隐藏税务机关的收入更加困难。我已经看到评论说这是西班牙和葡萄牙的要求,如果不是许多其他国家的要求,我也不会感到惊讶。

         

那么,如果我们接受它是一个有效的要求,在什么情况下无差距系列*的数字是一个问题?小组认为通常会让你相信它总是如此,但实际上它只是在特定情况下的一个潜在问题。

  
  1. 这一系列数字必须没有差距。
  2. 多个流程创建与数字相关联的实体(例如发票)。
  3. 必须在创建实体时生成数字。
  4.   
        

    如果必须满足所有这些要求,那么您的应用程序中有一个序列化点,我们马上讨论。

             

    首先让我们谈谈实现一系列数字要求的方法,如果你可以放弃这些要求中的任何一个。

             

    如果您的一系列数字可能存在差距(并且您有多个流程需要立即生成数字),那么请使用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),并使用唯一的密钥保护发票#,并可能检查是否缺少如果你真的非常关心,可以使用单独的代码。

             

    如果您不需要立即生成数字(但是您需要它们无间隙且多个进程生成实体),那么您可以允许生成实体并提交事务,然后将生成的数字留给单个批处理作业。实体表的更新,或插入单独的表。

             

    那么,如果我们需要通过多个流程即时生成无间隙系列数字的三连胜?我们所能做的就是尽量减少流程中的序列化时间,我提供以下建议,并欢迎任何其他建议(当然还有反建议)。

      
    1. 将您当前的值存储在专用表中。不要使用序列。
    2. 确保所有进程使用相同的代码通过将其封装在函数或过程中来生成新数字。
    3. 使用DBMS_Lock序列化对数字生成器的访问,确保每个系列都有自己的专用锁。
    4. 通过释放提交锁定
    5. ,保持系列生成器中的锁,直到您的实体创建事务完成
    6. 延迟生成号码直到最后一刻。
    7. 在生成数字之后和提交完成之前考虑意外错误的影响 - 应用程序是否会正常回滚并释放锁定,还是会锁定系列生成器,直到会话断开连接?无论使用何种方法,如果交易失败,则必须将序列号“返回到池中”。
    8. 你可以将整个事物封装在实体表的触发器中吗?您可以将它封装在一个表或其他API调用中,该调用会插入行并自动提交插入吗?
    9. Original article

答案 1 :(得分:2)

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

  1. 并行流程回滚
  2. 锁定超时
  3. 你会明白为什么。

答案 2 :(得分:1)

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

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

更多信息here

答案 3 :(得分:0)

2006年,有人发布了PostgreSQL邮件列表的无间隙序列解决方案:http://www.postgresql.org/message-id/44E376F6.7010802@seaworthysys.com