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



我有一个用于发票的 SaaS 宠物项目。在其中,我希望我的客户每个人都以票号1001开头。显然,我不能在 Postgres 中使用简单的自动字段,而只是在值中添加 1000,因为我的所有客户端都将共享相同的数据库和相同的tickets表。我尝试使用整数列类型和查询(伪 SQL)SELECT LATEST number FROM tickets WHERE client_id = [current client ID]来获取最新的数字,然后使用该数字+ 1来获取下一个number。问题在于,在并发的情况下,两个工单很容易以相同的数字结尾。我需要能够在 Django 或原始 SQL 中执行此操作的数字(与使用 Bash 或其他任何类似的东西相比)。

不是在寻找一种方法来强迫我的例子起作用。我只是在寻找一种解决方案来解决需要为每个客户独立增加票号的问题。

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

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

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

这将一步到位地做三件事

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

有了这个新号码,您现在可以插入一张新票证。如果事务被提交,它还将释放cust_numbers表上的锁,因此其他"等待数字"的事务可以继续进行。

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

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

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

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

您可以为每个客户创建序列,然后将列的值设置为 nextval('name_of_the_sequence') 。这实际上是serial的工作方式;在您的情况下,唯一的区别是您不使用列的默认值,并且具有多个序列。

创建这些序列,在插入新行时选择正确的序列可以通过 PL/Pgsql 过程很好地完成。

最新更新