dar*_*123 14 postgresql transactions serializable
在我的理解中,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);
Run Code Online (Sandbox Code Playgroud)
表数据是这样的:
id | user_id | type | balance
----+---------+----------+---------
1 | 1 | checking | 1000
2 | 1 | saving | 1000
3 | 2 | checking | 1000
4 | 2 | saving | 1000
Run Code Online (Sandbox Code Playgroud)
现在我为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;
Run Code Online (Sandbox Code Playgroud)
用户2是相同的,除了user_id = 2in where:
begin;
update accounts set balance = balance - 200 where user_id = 2 and type = 'checking';
select sum(balance) from accounts where user_id = 2;
commit;
Run Code Online (Sandbox Code Playgroud)
我首先提交用户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.
Run Code Online (Sandbox Code Playgroud)
我的问题是:
Lau*_*lbe 13
您可以使用以下索引解决此问题:
CREATE INDEX accounts_user_idx ON accounts(user_id);
Run Code Online (Sandbox Code Playgroud)
由于示例表中的数据太少,您必须告诉PostgreSQL使用索引扫描:
SET enable_seqscan=off;
Run Code Online (Sandbox Code Playgroud)
现在你的例子将起作用!
如果这看起来像黑魔法,请查看您SELECT和UPDATE语句的查询执行计划.
如果没有索引,两者都将在表上使用顺序扫描,从而读取表中的所有行.因此,两个交易最终都会SIReadLock在整个表格中出现.
这会触发序列化失败.
| 归档时间: |
|
| 查看次数: |
1006 次 |
| 最近记录: |