作者:瀚高PG实验室(Highgo PG Lab)-Chrisx # 从并发控制(MVCC)角度看deadtuple死数据的产生 @[toc] ## tuple结构 数据结构 HeapTupleHeaderData 是多版本并发控制的核心数据结构 | | | | | | | | | | | ------ | ------ | ----- | ------ | ----------- | ---------- | ------ | ----------- | --------- | | t_xmin | t_xmax | t_cid | t_ctid | t_infomask2 | t_infomask | t_hoff | null_bitmap | user_data | 虽然 [HeapTupleHeaderData]结构包含7个元素,但本文中只涉及其中4个元素。 * t_xmin 记录插入此元组的事务ID(txid)。 * t_xmax 记录删除或更新此元组的事务ID(txid)。如果这个元组没有被删除或更新,t_xmax被设置为0,这意味着INVALID。 * t_cid 记录命令ID(command id,cid),从0开始递增,表示当前事务中执行此命令之前执行了多少个SQL命令。例如,假定我们在单个事务中执行三个INSERT命令:BEGIN; INSERT; INSERT; INSER;COMMIT;。如果第一个命令插入这个元组,则t_cid被设置为0,如果第二个命令插入该元组,则t_cid被设置为1,依此类推。 * t_ctid 记录指向自身或新元组的元组标识符(tuple identifier,tid)。tid用于标识表中的元组。当这个元组更新时,这个元组的t_ctid指向新的元组; 否则,t_ctid指向自己。 ## tuple增删改及dead tuple产生 ### 1. Insert 插入操作中,新元组将直接插入目标表页面中。其 xmin 字段被存储为本事务的 XID,xmax 为 0,当事 务提交后,所有的事务的 XID 大于等于 xmin 中存储的 XID 的事务,都可以看到这条记录。 这完全符合 read commit 事务隔离级别的要求。 ```sql begin; insert into test_con values (1,'A'); commit; ``` **PostgreSQL提供了一个扩展pageinspect,用于显示page页的内容** ```sql CREATE EXTENSION pageinspect; create table test_con(id int,name text); insert into test_con values (1,'A'); test=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid FROM heap_page_items(get_raw_page('test.test_con', 0)); tuple | t_xmin | t_xmax | t_cid | t_ctid -------+--------+--------+-------+-------- 1 | 594 | 0 | 0 | (0,1) (1 row) ``` * t_xmin设置为594,表示这条数据是由事务594插入的 * t_xmax设置为0,保留事务id,无效的。表示这行数据没有被update或delete * t_cid设置为0,表示这行数据是事务594插入的第一行数据 * t_ctid设置为(0,1),指向自己,没有新版本产生 :warning: 注:page结构不在本文讨论,参考体系结构-物理结构 ### 2. delete 如果该记录被删除,在 PostgreSQL 中,暂时不会删除这条记录,而是会在这条记录上 做一个标识。PostgreSQL 的做法是将该记录的 xmax 设置为删除这条记录的事务的 XID。 这样,所有的该记录删除后的事务的 XID 都大于 xmax 的值,因此删除后发起的查询都无 法读取到这条记录;而对于删除这条记录之前的启动的查询,由于 XID 小于 xmax,因此仍 然可以读取到这条记录。这样就解决了 MVCC 的事务隔离和一致性读的问题。 ```sql begin; delete from test_con where id=1; commit; ``` 可以通过扩展 pageinspect ,查看page的内容 ```sql test=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid FROM heap_page_items(get_raw_page('test.test_con', 0)); tuple | t_xmin | t_xmax | t_cid | t_ctid -------+--------+--------+-------+-------- 1 | 594 | 595 | 0 | (0,1) (1 row) ``` * t_xmax设置为595,表示这行数据被事务595update或delete * 如果事务操作commited,那么这行数据tuple_1就不再需要了,会被标记为 `dead tuple`。 ### 3. update 如果该记录被修改(update)了,那么 PostgreSQL 不会直接修改原有的记录,而是会 生成一条新的记录,新记录的 xmin 为 update 操作的 XID,xmax 为 0,同时会将老记录的 xmax 设置为当前操作的 XID,也就是说新记录的 xmin 和老记录的 xmax 相同。这样在同 一张表中,同一条记录就会有存在多个副本。 ```sql test=> insert into test_con values (1,'A'); INSERT 0 1 test=> update test_con set name='B' where id=1; UPDATE 1 test=> update test_con set name='C' where id=1; UPDATE 1 test=> ``` 可以通过扩展 pageinspect ,查看page的内容 ```sql test=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid FROM heap_page_items(get_raw_page('test.test_con', 0)); tuple | t_xmin | t_xmax | t_cid | t_ctid -------+--------+--------+-------+-------- 1 | 599 | 600 | 0 | (0,2) 2 | 600 | 601 | 0 | (0,3) 3 | 601 | 0 | 0 | (0,3) (3 rows) ``` * tuple_1 t_xmax设置为600,被事务600修改 t_ctid设置为(0,2),不再指向自己,指向第二个版本tuple_2 * tuple_2 t_xmax设置为601,被事务601修改 t_ctid设置为(0,3),不再指向自己,指向第三个版本tuple_3 * tuple_3 t_xmax设置为0,没有被修改过 t_ctid设置为(0,3),指向自己 如果事务操作committed,那么数据tuple_1和tuple_2就不再需要了,会被标记为`dead tuple`。 <!-- 思考 产生这么多的dead tuple怎么办?死数据导致表膨胀,不断占用磁盘空间 --> 从MVCC 机制工作原理来看,INSERT 操作并没有太多的问题, PostgreSQL 的 INSERT 操作和其他数据库的工作原理十分类似,只是 PostgreSQL 的行头 大小为 20 字节,远远大于 Oracle 的 3 字节,因此 PostgreSQL 的存储额外开销要略大于 Oracle。从 UPDATE 操作来看,无论 UPDATE 多少个字段,PostgreSQL 都需要插入一条 新的记录,这样会造成 SEGMENT 高水位的增长,如果某张表的数据插入后,需要多次 UPDATE,那么这张表的高水位会出现暴涨。为了解决这个问题,PostgreSQL 使用了一个 版本回收机制----VACUUM。通过 VACUUM,PostgreSQL 可以回收旧版本,从而避免多版本带 来的性能问题。
从并发控制(MVCC)角度看deadtuple死数据的产生
来源:这里教程网
时间:2026-03-14 20:18:31
作者:
编辑推荐:
下一篇:
相关推荐
-
雷神推出 MIX PRO II 迷你主机:基于 Ultra 200H,玻璃上盖 + ARGB 灯效
2 月 9 日消息,雷神 (THUNDEROBOT) 现已宣布推出基于英
-
制造商 Musnap 推出彩色墨水屏电纸书 Ocean C:支持手写笔、第三方安卓应用
2 月 10 日消息,制造商 Musnap 现已在海外推出一款 Oce
热文推荐
- 1.1 Logical Structure of Database Cluster
- CentOS 7.8安装PostgreSQL(生产系统)
CentOS 7.8安装PostgreSQL(生产系统)
26-03-14 - 1.2 Physiacel Structure of Database Cluster
- Spring整合Activiti,在瀚高数据库初始化时指定schema解决方案
- PostgreSQL的xlog/Wal归档及日志清理
PostgreSQL的xlog/Wal归档及日志清理
26-03-14 - PostgreSQL的两个模板库
PostgreSQL的两个模板库
26-03-14 - 模型思维(01)
模型思维(01)
26-03-14 - postgreSQL数据库同步流复制和异步流复制控制
postgreSQL数据库同步流复制和异步流复制控制
26-03-14 - PostgreSQL中的触发器
PostgreSQL中的触发器
26-03-14 - 流复制浅析 —— 主备切换
流复制浅析 —— 主备切换
26-03-14
