Postgresql Postgres函数中的锁定

Postgresql Postgres函数中的锁定,postgresql,synchronization,locking,Postgresql,Synchronization,Locking,假设我有一个事务表和事务汇总表。我创建了以下触发器来更新事务汇总表 问题:会有死锁吗?根据我的理解,可能会发生这样的情况: _________ |__table__| <- executor #1 waits on executor #2 to be able to lock the whole table AND |_________| executor #2 waits on executor #1 to be able to lock the whole table |______

假设我有一个事务表和事务汇总表。我创建了以下触发器来更新事务汇总表

问题:会有死锁吗?根据我的理解,可能会发生这样的情况:

_________
|__table__| <- executor #1 waits on executor #2 to be able to lock the whole table AND 
|_________| executor #2 waits on executor #1 to be able to lock the whole table
|_________|
|_________| <- row is locked by executor #1
|_________| 
|_________| <- row is locked by executor #2

似乎唯一的选择是每次在事务开始时锁定整个表。

您的“从事务中选择1,其中…”是指访问“事务\u摘要”吗?另外,请注意,如果两个DB事务插入两个“事务”行,其中new.sender1=new.receiver2和new.receiver1=new.sender2,那么这两个查询至少在理论上可以互相死锁

通常,您不能保证不会从数据库中获得死锁。即使您试图通过仔细编写查询(例如,排序更新)来阻止它们,您仍然可能被发现,因为您无法控制插入/更新或约束检查的顺序。在任何情况下,将每个事务与其他事务进行比较以检查死锁不会随着应用程序的增长而扩展

因此,当您发现“死锁检测”错误时,您的代码应该随时准备重新运行事务。如果您这样做,并且您认为冲突事务将不常见,那么您最好让死锁处理代码来处理它

如果您认为死锁很常见,那么它可能会给您带来性能问题——尽管争夺一个大表锁也可能是这样。以下是一些选项:

例如,如果new.receiver和new.sender是MyUsers表中的行id,则可以要求插入到“transactions\u summary”中的所有代码首先执行“从MyUsers中选择1,其中id在user1中,user2用于更新”。如果有人忘记了,它会坏掉,但你的桌子也会坏掉。通过这种方式,您可以将一个大表锁交换为多个单独的行锁。 向事务摘要添加唯一约束,并在违反时查找错误。无论如何,您可能都应该添加约束,即使您以另一种方式处理此问题。它会检测到虫子。 您可以允许重复的事务汇总行,并要求该表的用户将它们相加。对于不知道如何创建bug的开发人员来说,虽然可以添加一个视图来执行添加操作,但这很混乱,也很容易。但是如果你真的不能承受锁定和死锁带来的性能损失,你可以这样做。 您可以尝试使用可序列化事务隔离级别并取出表锁。根据我的阅读,选择。。。FOR UPDATE应该创建一个谓词锁,普通SELECT也应该创建一个谓词锁。这将阻止执行冲突插入的任何其他事务成功提交。但是,在整个应用程序中使用SERIALIZABLE会降低性能,并使您有更多事务需要重试。 以下是可序列化事务隔离级别的工作原理:

create table test (id serial, x integer, total integer); ...
交易1:

DB=# begin transaction isolation level serializable;
BEGIN
DB=# insert into test (x, total) select 3, 100 where not exists (select true from test where x=3);
INSERT 0 1
DB=# select * from test;
id | x | total
----+---+-------
1 | 3 |   100
(1 row)
DB=# commit;
COMMIT
事务2,与第一行交错的行:

DB=# begin transaction isolation level serializable;
BEGIN
DB=# insert into test (x, total) select 3, 200 where not exists (select true from test where x=3);
INSERT 0 1
DB=# select * from test;
 id | x | total
----+---+-------
  2 | 3 |   200
(1 row)
DB=# commit;
ERROR:  could not serialize access due to read/write dependencies among transactions
DETAIL:  Reason code: Canceled on identification as a pivot, during commit attempt.
HINT:  The transaction might succeed if retried.

您是否尝试将事务\u摘要加入到锁定查询中,并应用更新事务\u摘要?如果在事务摘要中有定义良好的主键,则可以为插入保留整个表锁。此外,您可以使用找到的特殊变量,而不是rec_cnt变量。另外,您想用它实现什么?也许是多线程安全的升级@是的,我想要的是一个线程安全的upsert操作。在事务表中插入一次可导致事务汇总表上的两次更新。谢谢,我不知道有特殊变量FOUND@Pozs-看来join可能会有帮助。我要试试看@POZ-选择更新和加入确实有帮助-我在一些罕见的情况下遇到了死锁,它必须是“从事务摘要中选择1,其中…”我简化了我的实际工作代码并犯了一个错误:+1是一个很好的示例,我将其标记为已接受,因为您回答了我关于死锁的问题
DB=# begin transaction isolation level serializable;
BEGIN
DB=# insert into test (x, total) select 3, 200 where not exists (select true from test where x=3);
INSERT 0 1
DB=# select * from test;
 id | x | total
----+---+-------
  2 | 3 |   200
(1 row)
DB=# commit;
ERROR:  could not serialize access due to read/write dependencies among transactions
DETAIL:  Reason code: Canceled on identification as a pivot, during commit attempt.
HINT:  The transaction might succeed if retried.