Postgres在事务中锁定

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事务中的事务需要阻塞表呢?

我看不出在这个任何讨论文档交易的PG.

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删除的"死元组",导致大量空白空间(表膨胀).但是,如果您愿意使用表和索引,这些表和索引扫描将至少需要两倍的时间,这是一个选项.