ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] # <span style="font-size:15px">**元组结构**</span> 元组由三部分组成,即HeapTupleHeaderData结构、空位图和用户数据。 ![](https://img.kancloud.cn/5c/ea/5ceab2d3e2887ac868084ca3fb2e9f07_987x194.png) * t_xmin:保存插入(insert)该元组的事务的txid。 * t_xmax:保存删除或更新此元组的事务的txid。如果此元组尚未被删除或更新,则t_xmax设置为0,这意味着无效。 * t_cid:保存命令id(cid)。这意味着在从0开始的当前事务中执行此命令之前执行了多少SQL命令。例如,假设我们在一个事务中执行三个INSERT命令:'BEGIN; INSERT; INSERT; INSERT; COMMIT;'。如果第一个命令插入这个元组,则t_cid设置为0。如果第二个命令插入此命令,则t_cid设置为1,依此类推。当这个元组被更新时,这个元组的t_ctid指向新的元组;否则,t\_ctid会指向自己。 # <span style="font-size:15px">**insert tuple**</span> 通过插入操作,一个新元组被直接插入到目标表的页面中 ![](https://img.kancloud.cn/58/fa/58fac74698858294cbf89a2af0605ab7_1031x227.png) Tuple_1: 1. t_xmin被设置为99,因为这个元组是由txid 99插入的。 2. t_xmax设置为0,因为此元组尚未删除或更新。 3. t_cid设置为0,因为此元组是txid 99插入的第一个元组。 4. t_ctid设置为(0,1),它指向自身,因为这是最新的元组。 <br> # <span style="font-size:15px">**delete tuple**</span> Tuple_1被 txid=111的事务删除 ![](https://img.kancloud.cn/a4/b1/a4b18dd8f0ed420da8d20dcdeba245f1_991x230.png) Tuple\_1: &nbsp;&nbsp;&nbsp;&nbsp;t_xmax设置为111。 &nbsp;&nbsp;&nbsp;&nbsp;如果提交了txid 111,则不再需要Tuple_1。在PostgreSQL中,不需要的元组通常被称为死元组。 <br> # <span style="font-size:15px">**update tuple**</span> 在更新操作中,PostgreSQL从逻辑上删除最新的元组并插入一个新元组 ![](https://img.kancloud.cn/a2/ea/a2eadc9fdabd4d229280b735f4316470_1002x456.png) 假设txid 99插入的行被txid 100更新了两次。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当执行第一个更新命令时,通过将txid 100设置为t_xmax,逻辑上删除Tuple_1,然后插入Tuple_2。然后,将Tuple_1的t_ctid重写为指向Tuple_2。Tuple_1和Tuple_2的头字段如下所示。 Tuple_1: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t_xmax设置为100。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t_ctid从(0,1)重写为(0,2)。 Tuple_2: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t_xmin设置为100。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t_xmax设置为0。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t_cid设置为0。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t_ctid设置为(0,2)。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当执行第二个更新命令时,与第一个更新命令一样,逻辑上删除Tuple_2,插入Tuple_3。Tuple_2和Tuple_3的头字段如下所示。与删除操作一样,如果提交了txid 100,则Tuple_1和Tuple_2将是死元组,如果中止了txid 100,则Tuple_2和Tuple_3将是死元组。 Tuple_2: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t_xmax设置为100。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t_ctid从(0,2)重写为(0,3)。 Tuple_3: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t_xmin设置为100。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t_xmax设置为0。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t_cid设置为1。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;t_ctid设置为(0,3)。 # <span style="font-size:15px">**通过pageinspect扩展查看tuple**</span> ``` // 安装pageinspect扩展 postgres=# CREATE EXTENSION pageinspect; CREATE EXTENSION // 建表并插入两条数据 postgres=# CREATE TABLE tbl (data text); CREATE TABLE postgres=# INSERT INTO tbl VALUES('A'); INSERT 0 1 postgres=# INSERT INTO tbl VALUES('B'); INSERT 0 1 // 可以查看到两条数据的元组头部和元组原始数据 postgres=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid FROM heap_page_items(get_raw_page('tbl', 0)); tuple | t_xmin | t_xmax | t_cid | t_ctid -------+--------+--------+-------+-------- 1 | 493 | 0 | 0 | (0,1) 2 | 494 | 0 | 0 | (0,2) (2 rows) // 删除 B 数据之后,B数据已被标识删除,dead tuple,待回收 postgres=# delete from tbl where data = 'B'; DELETE 1 postgres=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid FROM heap_page_items(get_raw_page('tbl', 0)); tuple | t_xmin | t_xmax | t_cid | t_ctid -------+--------+--------+-------+-------- 1 | 493 | 0 | 0 | (0,1) 2 | 494 | 495 | 0 | (0,2) (2 rows) //更新A数据。逻辑上删除Tuple_1,然后插入Tuple_3 postgres=# update tbl set data = 'C' where data = 'A'; UPDATE 1 postgres=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid FROM heap_page_items(get_raw_page('tbl', 0)); tuple | t_xmin | t_xmax | t_cid | t_ctid -------+--------+--------+-------+-------- 1 | 493 | 496 | 0 | (0,3) 2 | 494 | 495 | 0 | (0,2) 3 | 496 | 0 | 0 | (0,3) (3 rows) // 手动vacuum进行垃圾回收 postgres=# vacuum; VACUUM postgres=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid FROM heap_page_items(get_raw_page('tbl', 0)); tuple | t_xmin | t_xmax | t_cid | t_ctid -------+--------+--------+-------+-------- 1 | | | | // line pointers不会被回收 2 | | | | // 会产生空间碎片 3 | 496 | 0 | 0 | (0,3) (3 rows) // 全量垃圾回收,彻底回收,不会有空间碎片,重写表 postgres=# vacuum full; VACUUM postgres=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid FROM heap_page_items(get_raw_page('tbl', 0)); tuple | t_xmin | t_xmax | t_cid | t_ctid -------+--------+--------+-------+-------- 1 | 496 | 0 | 0 | (0,1) (1 row) ```