使用Serializable隔离在PostgreSQL 9.2.1中进行谓词锁定

时间:2012-10-11 10:40:20

标签: postgresql transactions locking

我在the postgres documentation on transaction isolation中一直在仔细阅读other of my questions,但我仍然无法理解“谓词锁定”的内容。

我希望有人能够启发我: - )

根据文档: PostgreSQL中的谓词锁与大多数其他数据库系统一样,基于事务实际访问的数据

这听起来不错,那为什么会发生以下情况?

CREATE TABLE mycustomer(cid integer PRIMARY KEY, licenses integer);
CREATE TABLE mydevice(id integer PRIMARY KEY, cid integer REFERENCES 
mycustomer (cid), status varchar(10));

INSERT INTO mycustomer(cid, licenses) VALUES (1, 5);
INSERT INTO mycustomer(cid, licenses) VALUES (2, 5);

    Request 1                            Request2
BEGIN TRANSACTION ISOLATION 
LEVEL SERIALIZABLE;
                                         BEGIN TRANSACTION ISOLATION 
                                         LEVEL SERIALIZABLE;
SELECT * from mydevice where cid = 1;

                                         SELECT * from mydevice where cid = 2;
INSERT INTO mydevice(id, cid, status) 
VALUES (1, 1, 'ok');

                                         INSERT INTO mydevice(id, cid, status)         
                                         VALUES (2, 2, 'ok');
commit;
(=ok)                                 
                                         commit;
                                         (=rollback)

我理解来自请求1和请求2的插入与先前的读取不冲突,因此不应该启动任何错误。为什么我得到“错误:由于事务之间的读/写依赖性而无法序列化访问”。

您可以想象我不会发生上述行为,因为无论其详细信息如何,每个并发请求都将得到支持。在我的业务场景中,我希望并发请求在为同一个客户插入数据时(根据示例设备)仅进行回滚支持。

这些操作是从Java应用程序执行的。原则上我正在考虑创建一个锁定表来满足我的需求。有什么想法吗?

非常感谢!

2 个答案:

答案 0 :(得分:16)

来自Transaction Isolation页面:

  

在执行查询期间获取的特定锁将取决于查询使用的计划,并且在期间可以将多个更细粒度的锁(例如,元组锁)组合成更少的粗粒度锁(例如,页锁)。事务的过程,以防止用于跟踪锁的内存耗尽。

     

...

     
      
  • 顺序扫描总是需要关系级谓词锁定。这可能导致序列化失败率增加。
  •   

EXPLAIN上的SELECT可以告诉您查询计划的内容,但如果表格很小(或为空!),PostgreSQL几乎肯定会选择顺序扫描而不是引用指数。这将导致整个表上的谓词锁定,导致序列化失败,只要另一个事务对表执行任何操作。

在我的系统上:

isolation=# EXPLAIN SELECT * from mydevice where cid = 1;
                        QUERY PLAN                        
----------------------------------------------------------
 Seq Scan on mydevice  (cost=0.00..23.38 rows=5 width=46)
   Filter: (cid = 1)
(2 rows)

您可以尝试添加索引并强制它使用:

isolation=# CREATE INDEX mydevice_cid_key ON mydevice (cid);
CREATE INDEX
isolation=# SET enable_seqscan = off;
SET
isolation=# EXPLAIN SELECT * from mydevice where cid = 1;
                                    QUERY PLAN                                    
----------------------------------------------------------------------------------
 Index Scan using mydevice_cid_key on mydevice  (cost=0.00..8.27 rows=1 width=46)
   Index Cond: (cid = 1)
(2 rows)

然而,这不是正确的解决方案。让我们稍微回顾一下。

Serializable旨在保证事务具有与它们一个接一个地运行完全相同的效果,尽管事实上您实际上是在同时运行这些事务。 PostgreSQL没有无限的资源,所以虽然它确实将谓词锁放在查询实际访问的数据上,但“数据”可能意味着“返回的行”。

PostgreSQL选择在认为可能存在问题时标记序列化失败,而不是在确定时。 (因此它如何将行锁概括为页锁。)此设计选择会导致误报,例如示例中的误报。假阳性不太理想,但它不会影响隔离语义的正确性。

错误消息是:

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.

这个提示很关键。您的应用程序需要捕获序列化失败并重试整个操作。每当SERIALIZABLE正在运行时都是如此 - 它保证了串行正确性,尽管并发,但如果没有应用程序的帮助它就无法做到这一点。换句话说,如果您实际上正在进行并发修改,PostgreSQL满足隔离要求的唯一方法是让您的应用程序自行序列化。因此:

  

使用这种技术的环境有一个处理序列化失败的通用方法(总是返回SQLSTATE值为'40001'),这很重要,因为很难准确预测哪些事务可能会对读/写依赖关系,需要回滚以防止序列化异常。

答案 1 :(得分:1)

对于那些更好奇的人,在Postgres 9.1源代码中,如果你看一下src/backend/storage/lmgr/README-SSI,就会有很多关于谓词锁定和可序列化交易的详细描述

这是一个相同的片段:

  

可序列化快照隔离(SSI)和谓词锁定      ================================================== =========

     

此代码位于lmgr目录中,因为其中约90%是一个     执行谓词锁定,这是SSI所必需的,     而不是与SSI本身直接相关。当另一个使用     对于谓词锁定,可以证明可以理解这两件事    除此之外,这个自述文件应该可以拆分。

     

现金:

     

此功能由Kevin Grittner和Dan R. K. Ports开发,    来自Joe Conway,Heikki Linnakangas和他的评论和建议    杰夫戴维斯。它基于这些论文中发表的工作:

 Michael J. Cahill, Uwe Röhm, and Alan D. Fekete. 2008.
 Serializable isolation for snapshot databases.
 In SIGMOD '08: Proceedings of the 2008 ACM SIGMOD
 international conference on Management of data,
 pages 729-738, New York, NY, USA. ACM.
 http://doi.acm.org/10.1145/1376616.1376690

 Michael James Cahill. 2009.
 Serializable Isolation for Snapshot Databases.
 Sydney Digital Theses.
 University of Sydney, School of Information Technologies.
 http://hdl.handle.net/2123/5353