为啥两个具有隔离可序列化的事务在写入不同的行时被阻塞

Posted

技术标签:

【中文标题】为啥两个具有隔离可序列化的事务在写入不同的行时被阻塞【英文标题】:Why two transactions with isolation serializable are blocked when writing to different lines为什么两个具有隔离可序列化的事务在写入不同的行时被阻塞 【发布时间】:2020-03-23 16:18:19 【问题描述】:

我的应用中的交易有问题。

我的第一笔交易:

BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT * FROM public.owner WHERE id = 15;
UPDATE public.owner SET current_cat = 2 WHERE id = 15;
COMMIT;

我的第二笔交易:

BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT * FROM public.owner WHERE id = 16;
UPDATE public.owner SET current_cat = 4 WHERE id = 16;
COMMIT;

我的表格的粗略示例:

CREATE TABLE assortment.cat (
    id int not null,
    name varchar not null
);

CREATE TABLE assortment.owner (
    id int not null,
    fio varchar not null,
    current_cat int not null
);

所有者可以交换猫 :) 因此,如果当前可以逐步开始两个交易(第一个交易的开始 -> 第二个交易的开始 -> 选择第一个交易 -> 选择第二个交易等)那么第一个交易结束是成功但第二次交易会失败:

由于事务之间的读/写依赖性,无法序列化访问。 原因代码:在提交尝试期间,在识别为枢轴时被取消

两个事务改变了不同的行,为什么会发生阻塞?我希望这两项交易都能成功完成。

我将不胜感激! :)

附:我正在使用 PostgreSQL

【问题讨论】:

我认为你的question is answered here。 【参考方案1】:

你有关于 owner(id) 的索引吗?

如果我完全按照上面给出的方式创建表,我可以重现该问题:

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.

但是如果我这样创建它:

create table owner
(
id int not null primary key,
fio varchar not null,
current_cat int not null
);

我无法重现该问题。

查看第一个答案 Why does PostgreSQL serializable transaction think this as conflict?

【讨论】:

谢谢!你的链接帮助了我:)【参考方案2】:

可序列化的隔离级别保证不会出现误报。它不保证不会出现误报。如果必须执行后者,时间和/或内存使用量可能会爆炸。正确编写以使用可序列化隔离的代码必须始终准备好重试事务。因此,偶尔由于误报重试并不是什么大问题,除非它经常发生以至于成为性能问题。

这种类型的误报在微观玩具示例中比在真实案例中更可能发生。

【讨论】:

以上是关于为啥两个具有隔离可序列化的事务在写入不同的行时被阻塞的主要内容,如果未能解决你的问题,请参考以下文章

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

为啥在另一个快照隔离事务中插入具有引用行的外键引用行的行会导致事务挂起?

关于可序列化交易的一些疑问?

事务4-事务与锁

数据库事务隔离级别和锁实现机制

2Mysql事务的隔离级别?