Sim*_*kin 6 postgresql locking transactions postgresql-9.4
我无法理解锁与Postgres中的事务如何交互.
当我运行这个(长)查询时,我对发生的高度锁定感到惊讶:
BEGIN;
TRUNCATE foo;
\COPY foo FROM 'backup.txt';
COMMIT;
Run Code Online (Sandbox Code Playgroud)
该文件对于\COPY
没有提到它需要什么级别的锁,但是这篇文章表明,它只能得到一个RowExclusiveLock.但是当我在以下期间运行此查询时\COPY
:
SELECT mode, granted FROM pg_locks
WHERE relation='foo'::regclass::oid;
Run Code Online (Sandbox Code Playgroud)
我明白了:
mode granted
RowExclusiveLock true
ShareLock true
AccessExclusiveLock true
Run Code Online (Sandbox Code Playgroud)
AccessExclusiveLock来自哪里?我假设它来自TRUNCATE
,这需要一个AccessExclusiveLock.但是TRUNCATE
很快就完成了,所以我希望锁定能够快速释放.这给我留下了一些问题.
当事务中的命令获取锁时,是否在命令结束时(在事务结束之前)释放该锁?如果是这样,为什么我会观察到上述行为?如果没有,为什么不呢?实际上,由于事务直到事务才触及表COMMIT
,为什么TRUNCATE
事务中的事务需要阻塞表呢?
Lau*_*lbe 10
这里有一些误解需要澄清.
首先,事务在提交之前确实触及了表.你引用的评论说a ROLLBACK
(和a COMMIT
)也不会触及表格,这是不同的.它们在提交日志(in pg_clog
)中记录事务状态,并将COMMIT
事务日志刷新到磁盘(这是一个值得注意的例外TRUNCATE
,这与您的问题相关:旧表保留到事务结束并被删除期间COMMIT
).
如果所有更改都被阻止,直到COMMIT
并且不会进行锁定,COMMIT
则会非常昂贵,并且由于并发修改而常常会失败.事务必须像以前一样记住数据库的状态,并检查更改是否仍然适用.这种处理并发的方式称为乐观concurreny控件,虽然它对于应用程序来说是一个不错的策略,但对于关系数据库来说效果不好,关系数据库COMMIT
应该高效并且不应该失败(除非基础结构存在重大问题) ).
因此,关系数据库使用的是悲观并发控制或锁定,即它们在访问数据库对象之前将其锁定,以防止并发活动妨碍它们.
其次,关系数据库使用两阶段锁定,其中锁定(至少是用户可见的,所谓的重量级锁定)始终保持到事务结束.这是必要的(但不充分)将事务保持在逻辑顺序(可序列化)和一致性.如果释放一个锁,然后其他人删除了插入但未提交的行通过外键约束引用的行,该怎么办?
回答问题
所有这一切的结果是你的表将保持ACCESS EXCLUSIVE
锁定TRUNCATE
直到事务结束.是不是很明显为什么这是必要的?如果允许其他事务甚至在(尚未提交)之后读取该表TRUNCATE
,则它们会发现它为空,因为TRUNCATE
实际上清空了表并且不遵守MVCC语义.不允许这样的脏读(可能尚未回滚的未提交数据).
如果您在重新填充期间确实需要对表的读取权限,则可以使用DELETE
而不是TRUNCATE
.缺点是这是一个更昂贵的操作,将使表中有许多必须通过autovacuum删除的"死元组",导致大量空白空间(表膨胀).但是,如果您愿意使用表和索引,这些表和索引扫描将至少需要两倍的时间,这是一个选项.