繁体   English   中英

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

[英]Why does PostgreSQL serializable transaction think this as conflict?

据我了解,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

现在我为 2 个用户运行 2 个并发事务。 在每笔交易中,我都会在支票账户中减少一些钱,并检查该用户的总余额。 如果大于 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;

用户 2 是相同的,除了user_id = 2where

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,但是都没有效果。
  2. 这是否意味着可序列化事务不允许在同一张表上发生并发事务,即使它们的读/写没有冲突?
  3. 每个用户做某事很常见,我应该避免对发生频率很高的操作使用可序列化事务吗?

您可以使用以下索引解决此问题:

CREATE INDEX accounts_user_idx ON accounts(user_id);

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

SET enable_seqscan=off;

现在你的例子将起作用!

如果这看起来像黑魔法,请查看SELECTUPDATE语句的查询执行计划。

如果没有索引,两者都将在表上使用顺序扫描, 从而读取表中的所有行 因此,两个事务最终SIReadLock在整个表上显示一个SIReadLock

这会触发序列化失败。

我还遇到了 PostgreSQL 在可序列化隔离级别运行多个并发测试用例的问题,这些测试用例创建/更新了一些不相交的记录。

相同的测试用例通过了 CockroachDB。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM