PostgreSQL

关注公众号 jb51net

关闭
首页 > 数据库 > PostgreSQL > PostgreSQL 死元组

PostgreSQL 中的死元组(Dead tuple)详解

作者:ShiningStar_Li

PostgreSQL使用MVCC(多版本并发控制)来实现高并发和事务隔离,这与 MySQL (InnoDB) 的机制有很大不同,这篇文章介绍PostgreSQL 中的死元组(Dead tuple)的相关知识,感兴趣的朋友一起看看吧

为什么会有死元组?(MVCC机制)

PostgreSQL 使用 MVCC(多版本并发控制) 来实现高并发和事务隔离。这与 MySQL (InnoDB) 的机制有很大不同。

当你执行 UPDATE 时:

  1. PG 不会直接修改原来的那一行数据。
  2. PG 会将旧行标记为“死亡”(设置 xmax 事务ID)。
  3. PG 会插入一个全新的行,包含更新后的数据。
  4. 结果:表中同时存在“旧版本(死元组)”和“新版本(活元组)”。

当你执行 DELETE 时:

  1. PG 不会直接从磁盘移除该行。
  2. PG 只是将该行标记为“死亡”(设置 xmax 事务ID)。
  3. 结果:该行变成了死元组,依然占据磁盘空间。

为什么要这样做?为了事务隔离。
假设事务 A 正在读取某行数据,此时事务 B 更新了这行数据。

死元组的危害

如果死元组不及时清理,会带来严重后果:

表膨胀(Table Bloat):

查询性能下降:

事务 ID 回卷风险(Transaction ID Wraparound):

死元组何时被清理?

死元组不会自动消失,必须由 VACUUM 进程来清理。

清理的条件(Visibility Map)

VACUUM 只能清理那些对所有当前活跃事务都不可见的死元组。

清理的过程

注意:普通的 VACUUM 不会缩小表文件的大小(不会归还空间给操作系统),它只是让空间在内部可复用。只有 VACUUM FULL 才会真正缩小文件。

如何监控死元组?

你可以查询系统视图 pg_stat_user_tables 来查看表的死元组情况:

SELECT 
    relname AS table_name, 
    n_live_tup AS live_tuples,   -- 活元组数量
    n_dead_tup AS dead_tuples,   -- 死元组数量
    last_vacuum,                 -- 上次手动 vacuum 时间
    last_autovacuum,             -- 上次自动 vacuum 时间
    last_autoanalyze             -- 上次自动 analyze 时间
FROM pg_stat_user_tables 
ORDER BY n_dead_tup DESC;

如何处理过多的死元组?

依赖 Autovacuum(默认推荐)

PostgreSQL 有一个后台进程叫 autovacuum,它会自动检测表的变动并触发 VACUUM。

调优建议:对于高频更新的表,可以单独调整其存储参数:

ALTER TABLE ais41.recentships SET (autovacuum_vacuum_scale_factor = 0.05);

(默认是 0.2,即 20% 的死元组才触发;改为 0.05 表示 5% 就触发,更频繁地清理。)

杀死长事务

如前所述,长事务会阻止 VACUUM 清理死元组。

查找并终止长时间运行的事务:

SELECT pg_terminate_backend(pid) 
FROM pg_stat_activity 
WHERE state != 'idle' 
  AND now() - xact_start > interval '10 minutes';

手动 VACUUM

如果 Autovacuum 跟不上节奏,可以手动执行:

VACUUM VERBOSE ais41.recentships;

重建表(极端情况)

如果表已经极度膨胀(例如 90% 都是死元组),普通 VACUUM 效率太低。

到此这篇关于PostgreSQL 中的死元组(Dead tuple)详解的文章就介绍到这了,更多相关PostgreSQL 死元组内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

阅读全文