5

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

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

4

2 回答 2

5

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

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

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创建新客户时自动向表中插入一行。

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

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

于 2012-04-18T15:19:30.177 回答
2

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

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

于 2012-04-18T14:02:59.310 回答