问题 遇到序列化失败的条件是什么?


PostgreSQL手册页 在Serializable Isolation Level上:

[Like]可重复读取级别,使用此级别的应用程序必须准备好因序列化失败而重试事务。

在可重复读取或可序列化级别遇到序列化失败的条件是什么?

我尝试用两个实例引发序列化失败 psql 正在运行,但即使事务是由一个实例提交的,另一个实例在可序列化级别的事务中,而另一个实例在提交其更改时成功。两者都只是将记录插入表中,所以我可能需要尝试更复杂的东西。

基本上我试图了解在序列化失败的情况下会发生什么以及如何出现序列化失败。


6462
2017-10-09 17:39


起源



答案:


对于 REPEATABLE READ 这个例子会做:

准备阶段:

psql-0> CREATE TABLE foo(key int primary key, val int);
CREATE TABLE
psql-0> INSERT INTO foo VALUES(1, 42);

现在请关注psql-X 表示动作交错的部分:

psql-1> BEGIN ISOLATION LEVEL REPEATABLE READ;
psql-1> UPDATE foo SET val=val+1;
UPDATE 1
psql-2> BEGIN ISOLATION LEVEL REPEATABLE READ;
psql-2> UPDATE foo SET val=val+1;
*** no output, transaction blocked ***

psql-1> COMMIT;

psql-2> *** unblocks ***
ERROR:  could not serialize access due to concurrent update

一个例子 SERIALIZABLE 在PostgreSQL 9.1的文档中,从这里应该没问题。


5
2017-10-09 18:26



问题是,在我的逻辑中,val应该在两次交易结束时增加2。这就是实际发生的事情 在MySQL中 当使用REPEATABLE READ或SERIALIZABLE执行上述操作时。但是在PostgreSQL中,它给出了序列化错误。 PostgreSQL错误让我感到困惑,因为查询在事务结束之前从不读取(即不关心)val的最终值。那么为什么会出错呢? - Hendy Irawan
此外,“在PostgreSQL中,这些锁不会导致任何阻塞,因此无法在导致死锁方面发挥作用。”那么为什么psql-2在尝试UPDATE时被阻止了? - Hendy Irawan
@HendyIrawan:第一条评论:你必须始终考虑好的情况,但也可能考虑可能的坏情况。所以在你的逻辑中没有阻塞:No 1递增值,No 2读取它并将其当前值写入其他表并再次递增该值。然后No 1进行回滚。现在怎么办?这个值应该只增加一个吗?写入另一个表的“转义”值怎么样?它现在可能包含不一致的数据 - 它也必须递减 - 但这会影响另一个事务。因此,在ACID中对“原子性”和/或“同等性”说再见。 - A.H.
@HendyIrawan:第2条评论:您引用了有关“可序列化隔离级别”的文本,而不是“可重复读取”。因此,我在答案中的例子不适用于该文本。不要混合水平:-) - A.H.


答案:


对于 REPEATABLE READ 这个例子会做:

准备阶段:

psql-0> CREATE TABLE foo(key int primary key, val int);
CREATE TABLE
psql-0> INSERT INTO foo VALUES(1, 42);

现在请关注psql-X 表示动作交错的部分:

psql-1> BEGIN ISOLATION LEVEL REPEATABLE READ;
psql-1> UPDATE foo SET val=val+1;
UPDATE 1
psql-2> BEGIN ISOLATION LEVEL REPEATABLE READ;
psql-2> UPDATE foo SET val=val+1;
*** no output, transaction blocked ***

psql-1> COMMIT;

psql-2> *** unblocks ***
ERROR:  could not serialize access due to concurrent update

一个例子 SERIALIZABLE 在PostgreSQL 9.1的文档中,从这里应该没问题。


5
2017-10-09 18:26



问题是,在我的逻辑中,val应该在两次交易结束时增加2。这就是实际发生的事情 在MySQL中 当使用REPEATABLE READ或SERIALIZABLE执行上述操作时。但是在PostgreSQL中,它给出了序列化错误。 PostgreSQL错误让我感到困惑,因为查询在事务结束之前从不读取(即不关心)val的最终值。那么为什么会出错呢? - Hendy Irawan
此外,“在PostgreSQL中,这些锁不会导致任何阻塞,因此无法在导致死锁方面发挥作用。”那么为什么psql-2在尝试UPDATE时被阻止了? - Hendy Irawan
@HendyIrawan:第一条评论:你必须始终考虑好的情况,但也可能考虑可能的坏情况。所以在你的逻辑中没有阻塞:No 1递增值,No 2读取它并将其当前值写入其他表并再次递增该值。然后No 1进行回滚。现在怎么办?这个值应该只增加一个吗?写入另一个表的“转义”值怎么样?它现在可能包含不一致的数据 - 它也必须递减 - 但这会影响另一个事务。因此,在ACID中对“原子性”和/或“同等性”说再见。 - A.H.
@HendyIrawan:第2条评论:您引用了有关“可序列化隔离级别”的文本,而不是“可重复读取”。因此,我在答案中的例子不适用于该文本。不要混合水平:-) - A.H.


序列化失败有许多可能的原因。从技术上讲,两个事务之间的死锁是序列化失败的一种形式,如果存在对模式(数据库结构)的并发修改,则可能发生在任何隔离级别。既然你在询问PostgreSQL,你应该知道在PostgreSQL中,这种类型的序列化失败会从其他类型获得单独的SQLSTATE:'40P01'。所有其他序列化失败返回'40001'。这个答案的其余部分将集中在PostgreSQL中的这些非死锁变种。

在活动副本(“热备用”)之外,这些只能在两个更严格的隔离级别发生:REPEATABLE READ和SERIALIZABLE。在REPEATABLE READ级别,这些只能由于写入冲突而发生 - 两个并发事务尝试更新或删除相同(现有)行。进行尝试的第一个事务锁定行并继续。如果提交,则第二个事务因序列化失败而失败。如果第一个事务因任何原因回滚,则被阻止的事务将被释放以继续并将在该行上获取自己的锁。此行为与事务持续时间内的单个“快照”组合,也称为SNAPSHOT ISOLATION。

在PostgreSQL版本9.1之前,SERIALIZABLE事务的工作方式完全相同。从9.1开始PostgreSQL使用一种名为Serializable Snapshot Isolation的新技术来确保任何可序列化事务集的行为与之完全一致 一些 串行(一次一个)执行这些事务。在9.1中使用SERIALIZABLE事务时,您的应用程序应该为除ROLLBACK之外的任何语句的序列化失败做好准备 - 即使在只读事务中甚至在COMMIT上也是如此。有关更多信息,请参阅PostgreSQL文档页面 http://www.postgresql.org/docs/current/interactive/transaction-iso.html 或Wiki页面提供了如何在新的,更严格的隔离级别中发生序列化失败的示例 http://wiki.postgresql.org/wiki/SSI

如果使用Hot Standby功能,如果存在长时间运行的查询,则可能会在只读副本上发生序列化故障,维护稳定的数据视图需要数据库阻止复制太长时间。有一些配置设置可以让您平衡复制数据的“新鲜度”与长时间运行查询的容差。某些用户可能希望创建多个副本,以便他们可以拥有最新数据(甚至可能选择同步复制),同时允许另一个副本根据需要延迟服务长时间运行的查询。

编辑以提供另一个链接:标题为的论文 PostgreSQL中的可序列化快照隔离在第38届超大型数据库国际会议上提出的内容提供了比其他链接更多的细节和观点,以及为这一实施奠定基础的论文的参考。


6
2018-04-02 22:21





如果这有助于任何人,这里是Freenode上#postgresql的成绩单:

[14:36] <dtrebbien>    遇到序列化失败的条件是什么?

[14:36] <dtrebbien> ^遇到的条件是什么?   序列化失败?

[14:37] <dtrebbien>是否有可以识别的PostgreSQL开发人员   序列化失败的条件?

[14:38] <peerce>    http://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-SERIALIZABLE

[14:43] <dtrebbien>“任何并发序列化的集合   交易将具有与在a处运行交易相同的效果   时间”

[14:44] <dtrebbien> PostgreSQL有哪些规则   发动机跟着?

[14:44] <dtrebbien> I.e.如果更改了一行,是否会触发   失败?

[14:44] <johto> 9.1中的可序列化隔离模式确实如此   复杂

[14:45] <dtrebbien>我想。

[14:45] <dtrebbien>我也读过,Serializable等级是   某种程度上“固定”

[14:45] <RhodiumToad> dtrebbien:在9.1之前,基本规则是   如果事务尝试更改当前值不是的行   可见,这是一个失败

[14:46] <dtrebbien> RhodiumToad:这很有意思。

[14:46] <dtrebbien>另外,访问一个值,对吗?

[14:46] <selenamarie> dtrebbien:除了别的什么   说,其背后的基本前提是检测周期   依赖

[14:47] <dtrebbien>哦。

[14:50] <dtrebbien>公平地说,在9.1,规则   触发隔离级别已变得更加复杂   基本上减少了“误报”序列化异常?

[14:51] <johto>他们变得复杂,因为更简单   rulex没有捕获所有序列化异常

[14:51] <dtrebbien>啊!我懂了。

[14:51] <dtrebbien>所以这就是发行说明的原因   “固定”。

[14:52] <RhodiumToad> dtrebbien:访问不可见的值   这不是一个错误,因为它只是得到了可见的值   快照的时间。

[14:53] <RhodiumToad> dtrebbien:只读可序列化查询   只需查看数据库的快照时间的静态状态。

[14:54] <RhodiumToad> dtrebbien:除了小皱纹外   TRUNCATE,所有序列化问题都涉及读/写查询

[15:03] <dtrebbien> RhodiumToad,johto,selenamarie和peerce:   你介意我把这个对话的成绩单发给Stack吗?   溢出?

[15:07] <selenamarie> dtrebbien:当然:)

[15:07] <dtrebbien>我不知道它是否会对任何人有所帮助。它   威力。

[15:08] <selenamarie> dtrebbien:我发自Kevin的笔记   Grittner在这里谈到这个问题:    http://www.chesnok.com/daily/2011/03/24/raw-notes-from-kevin-grittners-talk-on-ssi/


4
2017-10-09 19:33