为什么PostgreSQL可序列化事务认为这是冲突?

svgewumm  于 2023-02-04  发布在  PostgreSQL
关注(0)|答案(2)|浏览(182)

据我所知,PostgreSQL使用某种监视器来猜测可序列化隔离级别是否存在冲突。许多例子都是关于在并发事务中修改同一资源的,可序列化事务工作得很好。但我想用另一种方式测试并发问题。
我决定测试2个用户修改自己的帐户余额,并希望PostgreSQL足够聪明,不会检测到它的冲突,但结果不是我想要的。
下面是我的表,有4个帐户属于2个用户,每个用户有一个支票帐户和一个储蓄帐户。

create table accounts (
  id serial primary key,
  user_id int,
  type varchar,
  balance numeric
);

insert into accounts (user_id, type, balance) values
  (1, 'checking', 1000),
  (1, 'saving', 1000),
  (2, 'checking', 1000),
  (2, 'saving', 1000);

表格数据如下所示:

id | user_id |   type   | balance
----+---------+----------+---------
  1 |       1 | checking |    1000
  2 |       1 | saving   |    1000
  3 |       2 | checking |    1000
  4 |       2 | saving   |    1000

现在我为两个用户运行两个并发事务。在每个事务中,我从支票帐户中减去一些钱,并检查该用户的总余额。如果大于1000,则提交,否则回滚。
用户1的示例:

begin;

-- Reduce checking account for user 1
update accounts set balance = balance - 200 where user_id = 1 and type = 'checking';

-- Make sure user 1's total balance > 1000, then commit
select sum(balance) from accounts where user_id = 1;

commit;

除了where中的user_id = 2之外,用户2是相同的:

begin;
update accounts set balance = balance - 200 where user_id = 2 and type = 'checking';
select sum(balance) from accounts where user_id = 2;
commit;

我首先提交用户1的事务,毫无疑问它成功了。当我提交用户2的事务时,它失败了。

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.

我的问题是:
1.为什么PostgreSQL认为这两个事务是冲突的?我为所有SQL添加了user_id条件,并且没有修改user_id,但是这些都没有效果。
1.这是否意味着可序列化事务不允许并发事务发生在同一个表上,即使它们的读/写没有冲突?
1.每个用户做一些事情是很常见的,我应该避免使用序列化事务的操作发生非常频繁?

6kkfgxo0

6kkfgxo01#

可以使用以下索引修复此问题:

CREATE INDEX accounts_user_idx ON accounts(user_id);

由于示例表中的数据太少,您必须告诉PostgreSQL使用索引扫描:

SET enable_seqscan=off;

现在你的例子将工作!
如果这看起来像黑魔法,那么请查看SELECTUPDATE语句的查询执行计划。
如果没有索引,两个事务都将对表使用顺序扫描,* 从而阅读表中的所有行 *,因此两个事务都将在整个表上以SIReadLock结束。
这会触发序列化失败。

5hcedyr0

5hcedyr02#

据我所知,serializable具有最高的隔离级别,因此并发级别最低。事务一个接一个地发生,没有并发。

相关问题