在Postgres中手动排序列的正确方法是什么?

oro*_*aki 5 sql django postgresql autofield sequence

我有一个SaaS宠物项目用于开发票.在这里面,我想我的客户每票号1001开始很显然,我不能在Postgres的用一个简单的汽车领域,只是加1000的值,因为我所有的客户端将共享同一个数据库和同一个tickets表.我尝试使用整数列类型和查询(伪SQL)SELECT LATEST number FROM tickets WHERE client_id = [current client ID]来获取最新的数字,然后使用该数字+ 1来获取下一个数字number.问题在于,通过并发,两张票很容易以这种方式以相同的数字结尾.我需要能够在Django中或使用原始SQL(使用Bash或其他任何类型)执行此操作的数字.

我不是在寻找一种强迫我的榜样工作的方法.我只是在寻找一个解决方案来解决我需要为每个客户端单独增加票号的问题.

a_h*_*ame 5

我不认为这个问题有"便宜"的解决方案.在多用户环境中唯一安全(但不一定快)的解决方案是为每个客户提供一个"计数器"表,其中包含一行.

在插入新票证之前,每个交易都必须首先锁定客户的条目,如下所示:

UPDATE cust_numbers
  SET current_number = current_number + 1
WHERE cust_id = 42
RETURNING current_number;
Run Code Online (Sandbox Code Playgroud)

这将一步完成三件事

  1. 增加该客户的当前"顺序"号码
  2. 锁定行,以便执行相同操作的其他事务必须等待锁定
  3. 返回该列的新值.

使用该新号码,您现在可以插入新票证.如果事务已提交,它还将释放对cust_numbers表的锁定,因此可以继续"等待数字"的其他事务.

您可以将这两个步骤(更新..返回和插入)包装到单个存储函数中,以便将其背后的逻辑集中.您的应用程序只会在select insert_ticket(...)不知道票号如何生成的情况下打电话.

您可能还希望在customer表上创建一个触发器,以便在创建cust_numbers新客户时自动将行插入表中.

这样做的缺点是您可以有效地序列化为同一客户插入新票证的交易.根据系统中插入的大小,这可能会成为性能问题.

编辑
另一个缺点是,如果新开发人员忘记了此问题,您不会被迫以可能导致问题的方式插入票证.

  • @orokusaki:正如我所描述的那样:只有该客户的行才会被锁定.第二个连接将等到第一个提交.是的,这会降低您的表现.但是你可以得到这样的数字(缓慢但安全)或者使用序列快速获得它们但是它们在所有客户中都是唯一的.你必须决定什么更重要 (2认同)