Jav*_*cia 15 postgresql locking transactions
我一直在仔细阅读其他问题中提到的关于事务隔离的postgres文档,但我还没有设法理解"谓词锁定"的东西.
我希望有人能开导我:-)
根据文档: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)
Run Code Online (Sandbox Code Playgroud)
我理解来自请求1和请求2的插入与先前的读取不冲突,因此不应该发起任何错误.为什么我得到"错误:由于事务之间的读/写依赖性而无法序列化访问".
您可以想象我不会发生上述行为,因为无论其详细信息如何,每个并发请求都将被回滚.在我的业务场景中,我希望并发请求仅在为同一个客户插入数据(根据示例设备)时进行回滚支持.
这些操作是从Java应用程序执行的.原则上我正在考虑创建一个锁定表来满足我的需求.有任何想法吗?
非常感谢!
wil*_*ynn 17
从" 事务隔离"页面:
在执行查询期间获取的特定锁将取决于查询使用的计划,并且多个细粒度锁(例如,元组锁)可以在过程期间组合成更少的粗粒度锁(例如,页锁).该事务用于防止用于跟踪锁的内存耗尽.
...
- 顺序扫描将始终需要关系级谓词锁.这可能导致序列化失败率增加.
一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)
Run Code Online (Sandbox Code Playgroud)
您可以尝试添加索引并强制它使用它:
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)
Run Code Online (Sandbox Code Playgroud)
但是,这不是正确的解决方案.让我们回顾一下.
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.
Run Code Online (Sandbox Code Playgroud)
这个提示很关键.您的应用程序需要捕获序列化失败并重试整个操作.无论何时SERIALIZABLE发挥都是如此- 它保证了串行正确性,尽管并发,但如果没有应用程序的帮助它就无法做到这一点.换句话说,如果您实际上正在进行并发修改,PostgreSQL满足隔离要求的唯一方法是让您的应用程序自行序列化.从而:
重要的是,使用此技术的环境具有处理序列化失败的通用方法(总是返回SQLSTATE值为'40001'),因为很难准确预测哪些事务可能对读/写有影响依赖关系并需要回滚以防止序列化异常.
| 归档时间: |
|
| 查看次数: |
4044 次 |
| 最近记录: |