互联网上有一些消息来源坚称idle in transaction
连接可能会阻止真空清理死元组,以下是一些示例:
处于空闲事务状态的事务可以持有阻止其他查询的锁。它还可以防止 VACUUM(包括 autovacuum)清理死行,从而导致索引或表膨胀或事务 ID 环绕。
长事务实际上不是问题——如果长事务和许多小变化必须存在,问题就开始了。请记住:长事务可能会导致 VACUUM 无法清除死行。
实际上,有很多,但从我的角度来看,这听起来绝对荒谬:在大多数情况下,事务隔离级别是读已提交,这反过来意味着不需要为此类事务保留死元组,而且,我找到了替代方案关于该主题的意见:
它并不是真正的长期事务,而是长期快照。当然,长时间运行的 select 或 insert 语句可以做到这一点。对于高于读提交的隔离级别,整个事务将保留快照直到其宕机,因此如果某些事务打开了一个可重复读事务,然后在没有提交的情况下休假,那将是一个问题。挂起的准备交易也会(如果您不知道准备交易是什么,那么您可能没有使用它们)。
或Pavel Luzanov 在 Cybertec 博文下的评论:
我相信长事务的示例仅适用于可重复读取(或可序列化)隔离级别。但默认情况下 BEGIN 使用已提交读。因此,在第一个会话中的 SELECT 完成后,VACUUM 将在会话 2 中的后续 UPDATE、DELETE 命令之后删除表中的死行。
@Bill Karwin在他的回答中实际上证实了这一点(谢谢!)
问题是:是否存在“有效”“非虚构”场景时idle in transaction
连接应被视为有害?(我不是在询问隔离级别高于已提交读的事务、事务或连接泄漏、长事务持有锁等)。
确实,事务本身并不会阻止 的进展
VACUUM
。VACUUM
仅当满足以下两个条件之一时,事务才会被阻止:该事务已分配一个事务 ID(即,它已修改该数据库中的某些内容)。
该事务保存数据库的快照。快照是一种数据结构,用于确定某个事务对哪些其他事务可见。快照保持打开状态
只要 SQL 语句正在运行(因此长时间运行的查询可能会阻止
VACUUM
进度)当有一个光标打开时
在事务的整个持续时间内,处于
REPEATABLE READ
或隔离级别SERIALIZABLE
您可以使用我有关该主题的文章中的查询来查看是否存在阻止
VACUUM
的进度的事务:事实上,读提交事务不会阻塞真空。
演示:
我创建了一个包含 2 20行的表。我们可以看到它有那么多活元组和 0 个死元组。
然后我在事务中查询它,但尚未提交。这样就建立了一个快照。
在第二个窗口中,我删除了一半的行。
因为我的第一个窗口的事务默认是读提交的,所以它会更新其快照以查看删除的结果。
我还没有提交该交易。
如果我非常迅速地测量死元组,我会发现它们仍然在表中徘徊。
我所要做的就是等待
autovacuum_naptime
autovacuum 运行。默认为 1 分钟。