我在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应用程序执行的。原则上我正在考虑创建一个锁定表来满足我的需求。有什么想法吗?
非常感谢!
答案 0 :(得分:16)
在执行查询期间获取的特定锁将取决于查询使用的计划,并且在期间可以将多个更细粒度的锁(例如,元组锁)组合成更少的粗粒度锁(例如,页锁)。事务的过程,以防止用于跟踪锁的内存耗尽。
...
- 顺序扫描总是需要关系级谓词锁定。这可能导致序列化失败率增加。
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