一尘不染

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

sql

我有一个用于发票的SaaS宠物项目。在其中,我希望我的客户以票证编号1001开头。显然,我不能在Postgres中使用简单的auto字段,而不能在值中添加1000,因为我的所有客户都将共享相同的数据库和相同的tickets表。我尝试使用整数列类型和查询(伪SQL)SELECTLATEST number FROM tickets WHERE client_id = [current clientID]来获取最新的数字,然后使用该数字+1来获取下一个number。问题在于并发性很容易使两张票以这种方式以相同的号码结尾。我需要能够在Django或原始SQL(与使用Bash或其他同类工具相比)中执行此操作的数量。

我不是在寻找一种方法来强迫我的榜样发挥作用。我只是在寻找一种解决方案,该问题需要为每个客户独立增加票证号。


阅读 129

收藏
2021-05-23

共1个答案

一尘不染

我认为没有解决此问题的“廉价”解决方案。在多用户环境中唯一安全(但不一定快速)的解决方案是拥有一个“计数器”表,每个客户一行。

每笔交易都必须先锁定客户的条目,然后再插入新票证,如下所示:

UPDATE cust_numbers
  SET current_number = current_number + 1
WHERE cust_id = 42
RETURNING current_number;

一步就能完成三件事

  1. 增加该客户的当前“顺序”号
  2. 锁定该行,因此执行相同操作的其他事务将不得不等待锁定
  3. 返回该列的新值。

使用该新号码,您现在可以插入新票证。如果提交了事务,它还将释放对cust_numbers表的锁定,因此其他“等待号码”的事务可以继续进行。

您可以将两个步骤(更新..返回和插入)包装到单个存储的函数中,以便集中进行此操作后的逻辑。您的应用程序只会在select insert_ticket(...)不知道票证号码如何生成的情况下进行调用。

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

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

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

2021-05-23