PG 9.4 行冻结信息的测试

简介: 有在网上找过一些关于vacuum_freeze_min_age参数是如何影响vacuum冻结旧行的文章。类似的参考资料可以参看链接: http://blog.csdn.net/luojinbai/article/details/45848381 但是自己在做类似的测试时发现,并没有出现相同的
有在网上找过一些关于vacuum_freeze_min_age参数是如何影响vacuum冻结旧行的文章。类似的参考资料可以参看链接:
http://blog.csdn.net/luojinbai/article/details/45848381


但是自己在做类似的测试时发现,并没有出现相同的结果,反复测试几次,仍然不同。后来咨询了下德哥说,看源码,行冻结的信息不再通过改变xmin来实现行冻结的信息。当时就觉得绝望了,对于一个完全不懂任何高级开发语言的人来说,这个等于对PG又多了个谜。后来想到,是不是可以通过查看page中的信息,来确认,冻结的信息到底是怎么反应到infomask中。最后想到还从来没有用过pageinspect呢。


看了下官网对pageinspect的介绍:
https://www.postgresql.org/docs/9.4/static/pageinspect.html
可以通过函数heap_page_items(page bytea)来查看行头部的信息内容。非常开心的发现,在这个章节中,里面有很负责任地指出可以在那些源码文件中找到相关的信息,接下来,就开始了我的第一次源码查找的经历。

官网有如下一段话:
See src/include/storage/itemid.h and src/include/access/htup_details.h for explanations of the fields returned.

看来这个就是关于行的详细信息了。因为知道冻结信息放在t_infomask中,所以,找到htup_details.sh后,先定位t_infomask的结构,有如下内容:

/*
 * information stored in t_infomask:
 */
#define HEAP_HASNULL            0x0001    /* has null attribute(s) */
#define HEAP_HASVARWIDTH        0x0002    /* has variable-width attribute(s) */
#define HEAP_HASEXTERNAL        0x0004    /* has external stored attribute(s) */
#define HEAP_HASOID                0x0008    /* has an object-id field */
#define HEAP_XMAX_KEYSHR_LOCK    0x0010    /* xmax is a key-shared locker */
#define HEAP_COMBOCID            0x0020    /* t_cid is a combo cid */
#define HEAP_XMAX_EXCL_LOCK        0x0040    /* xmax is exclusive locker */
#define HEAP_XMAX_LOCK_ONLY        0x0080    /* xmax, if valid, is only a locker */

 /* xmax is a shared locker */
#define HEAP_XMAX_SHR_LOCK    (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)

#define HEAP_LOCK_MASK    (HEAP_XMAX_SHR_LOCK | HEAP_XMAX_EXCL_LOCK | \
                         HEAP_XMAX_KEYSHR_LOCK)
#define HEAP_XMIN_COMMITTED        0x0100    /* t_xmin committed */
#define HEAP_XMIN_INVALID        0x0200    /* t_xmin invalid/aborted */
#define HEAP_XMIN_FROZEN        (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID)
#define HEAP_XMAX_COMMITTED        0x0400    /* t_xmax committed */
#define HEAP_XMAX_INVALID        0x0800    /* t_xmax invalid/aborted */
#define HEAP_XMAX_IS_MULTI        0x1000    /* t_xmax is a MultiXactId */
#define HEAP_UPDATED            0x2000    /* this is UPDATEd version of row */
#define HEAP_MOVED_OFF            0x4000    /* moved to another place by pre-9.0
                                         * VACUUM FULL; kept for binary
                                         * upgrade support */
#define HEAP_MOVED_IN            0x8000    /* moved from another place by pre-9.0
                                         * VACUUM FULL; kept for binary
                                         * upgrade support */
#define HEAP_MOVED (HEAP_MOVED_OFF | HEAP_MOVED_IN)

#define HEAP_XACT_MASK            0xFFF0    /* visibility-related bits */


其中关于冻结的信息就存放在HEAP_XMIN_FROZEN中了。而该部分信息时通过另外两个字段相与得出的:
HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID

那么如果某个行被冻结后,那么该位的值应该是3. 接下来进行测试。



新建一张表,并插入一条数据,同时查看通过age以及pageinspect的函数查看相关的信息:
test=# create table t (id int,vb text);
CREATE TABLE
test=# insert into t values (1,'test');
INSERT 0 1
test=# select ctid,age(xmin),xmin,xmax,* from t;
 ctid  | age | xmin | xmax | id |  vb  
-------+-----+------+------+----+------
 (0,1) |   1 | 1864 |    0 |  1 | test
(1 row)

test=# select * from heap_page_items(get_raw_page('t',0));
 lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid 
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------
  1 |   8152 |        1 |     33 |   1864 |      0 |        0 | (0,1)  |           2 |       2306 |     24 |        |      
(1 row)

t_infomask对应的的2306,是t_infomask对应的值,该值是十进制,转换成十六进制则是0x0902,对应的位信息是:
HEAP_HASVARWIDTH
HEAP_XMIN_COMMITTED
HEAP_XMAX_INVALID



插入第二条数据,让表里的数据行的年龄有些不同:
test=# insert into t values (2,'again test');
INSERT 0 1


刚刚插入行的t_infomask值2050对应的十六进制为0x0802,之所以没有HEAP_XMIN_COMMITTED位信息的原因是,关于事务状态信息是通过hint bits来体现的,刚刚插入的行并不会在事务结束时立刻更新hint bits,而是会在下一次查询或者更新等操作时才会设置,所以这才出现了没有HEAP_XMIN_COMMITTED信息的t_infomask,具体的文章介绍,可以查看另一片翻译文章:
https://yq.aliyun.com/articles/55682

test=# select * from heap_page_items(get_raw_page('t',0));
 lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid 
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------
  1 |   8152 |        1 |     33 |   1864 |      0 |        0 | (0,1)  |           2 |       2306 |     24 |        |      
  2 |   8112 |        1 |     39 |   1865 |      0 |        0 | (0,2)  |           2 |       2050 |     24 |        |      
(2 rows)

test=# select ctid,age(xmin),xmin,xmax,* from t;
 ctid  | age | xmin | xmax | id |     vb     
-------+-----+------+------+----+------------
 (0,1) |   2 | 1864 |    0 |  1 | test
 (0,2) |   1 | 1865 |    0 |  2 | again test
(2 rows)

当通过查询语句select ctid,age(xmin),xmin,xmax,* from t查看表中数据后,hint bits得到更新,此时的t_infomask变成了2306,即0x0902
test=# select * from heap_page_items(get_raw_page('t',0));
 lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid 
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------
  1 |   8152 |        1 |     33 |   1864 |      0 |        0 | (0,1)  |           2 |       2306 |     24 |        |      
  2 |   8112 |        1 |     39 |   1865 |      0 |        0 | (0,2)  |           2 |       2306 |     24 |        |      
(2 rows)



查看当前pg_class中relfrozenxid字段的值,随后该值会因为vacuum的触发而改变。
test=# select relfrozenxid from pg_class where relname= 't';
 relfrozenxid 
--------------
         1863
(1 row)

将vacuum_freeze_min_age设置成更小的值,方便查看在经过很少的事务之后,就能使得表中的行可以被冻结。
test=# set vacuum_freeze_min_age=5;
SET
test=# select txid_current();
 txid_current 
--------------
         1866
(1 row)

test=# select txid_current();
 txid_current 
--------------
         1867
(1 row)

test=# select txid_current();
 txid_current 
--------------
         1868
(1 row)

test=# select relfrozenxid from pg_class where relname= 't';
 relfrozenxid 
--------------
         1863
(1 row)

test=# select ctid,age(xmin),xmin,xmax,* from t;
 ctid  | age | xmin | xmax | id |     vb     
-------+-----+------+------+----+------------
 (0,1) |   5 | 1864 |    0 |  1 | test
 (0,2) |   4 | 1865 |    0 |  2 | again test
(2 rows)

test=# vacuum t;
VACUUM
test=# select ctid,age(xmin),xmin,xmax,* from t;
 ctid  | age | xmin | xmax | id |     vb     
-------+-----+------+------+----+------------
 (0,1) |   5 | 1864 |    0 |  1 | test
 (0,2) |   4 | 1865 |    0 |  2 | again test
(2 rows)

test=# select * from heap_page_items(get_raw_page('t',0));
 lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid 
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------
  1 |   8152 |        1 |     33 |   1864 |      0 |        0 | (0,1)  |           2 |       2306 |     24 |        |      
  2 |   8112 |        1 |     39 |   1865 |      0 |        0 | (0,2)  |           2 |       2306 |     24 |        |      
(2 rows)

以上信息没有发生变化,还需要再次执行一次txid_current函数。



test=# select relfrozenxid from pg_class where relname= 't';
 relfrozenxid 
--------------
         1864
(1 row)

test=# select txid_current();
 txid_current 
--------------
         1869
(1 row)

test=# vacuum t;
VACUUM
test=# select * from heap_page_items(get_raw_page('t',0));
 lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid 
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------
  1 |   8152 |        1 |     33 |   1864 |      0 |        0 | (0,1)  |           2 |       2818 |     24 |        |      
  2 |   8112 |        1 |     39 |   1865 |      0 |        0 | (0,2)  |           2 |       2306 |     24 |        |      
(2 rows)
此时可以发现,age为6的ctid为(0,1)的行的t_infomask值发生了改变,2818转换后是0x0b02,对应的位信息为:
HEAP_XMIN_FROZEN
HEAP_XMAX_INVALID
HEAP_HASVARWIDTH
这个时候就可以判断,t中的行被冻结了。


此时,relfrozenxid也发生了变化,该字段实际值,根据官方文档的描述:
    All transaction IDs before this one have been replaced with a permanent ("frozen") transaction ID in this table. This is used to track whether the table needs to be vacuumed in order to prevent transaction ID wraparound or to allow pg_clog to be shrunk.
根据自己的理解,该字段存放的是上一次全表vacuum的冻结截止XID,可能会有些偏差~~。
test=# select relfrozenxid from pg_class where relname= 't';
 relfrozenxid 
--------------
         1865
(1 row)

test=# select ctid,age(xmin),xmin,xmax,* from t;
 ctid  | age | xmin | xmax | id |     vb     
-------+-----+------+------+----+------------
 (0,1) |   6 | 1864 |    0 |  1 | test
 (0,2) |   5 | 1865 |    0 |  2 | again test
(2 rows)


尽管冻结了行, 但是并没有体现在xmin中,也就印证了冻结的信息不再通过xmin来体现,源码中也有类似的介绍:

/*
 * HeapTupleHeaderGetRawXmin returns the "raw" xmin field, which is the xid
 * originally used to insert the tuple.  However, the tuple might actually
 * be frozen (via HeapTupleHeaderSetXminFrozen) in which case the tuple's xmin
 * is visible to every snapshot.  Prior to PostgreSQL 9.4, we actually changed
 * the xmin to FrozenTransactionId, and that value may still be encountered
 * on disk.
 */
目录
相关文章
|
安全 测试技术 网络架构
【专栏】编写网络设备割接方案的七个步骤,包括明确割接目标、收集信息、制定计划、设计流程、风险评估、准备测试环境和编写文档。
【4月更文挑战第28天】本文介绍了编写网络设备割接方案的七个步骤,包括明确割接目标、收集信息、制定计划、设计流程、风险评估、准备测试环境和编写文档。通过实际案例分析,展示了如何成功完成割接,确保业务连续性和稳定性。遵循这些步骤,可提高割接成功率,为公司的网络性能和安全提供保障。
1624 0
|
存储 数据采集 分布式计算
Hadoop-17 Flume 介绍与环境配置 实机云服务器测试 分布式日志信息收集 海量数据 实时采集引擎 Source Channel Sink 串行复制负载均衡
Hadoop-17 Flume 介绍与环境配置 实机云服务器测试 分布式日志信息收集 海量数据 实时采集引擎 Source Channel Sink 串行复制负载均衡
258 1
|
开发框架 前端开发 JavaScript
ABP框架测试信息---Winform端、动态网站、Vue&Element管理后端等
ABP框架测试信息---Winform端、动态网站、Vue&Element管理后端等
|
安全 网络协议 网络安全
Python 渗透测试:黑客内外网信息收集.(帮助 得到信息攻击计算机内外网.)
Python 渗透测试:黑客内外网信息收集.(帮助 得到信息攻击计算机内外网.)
228 0
|
机器人 定位技术 C++
技术笔记:ROS中测试机器人里程计信息
技术笔记:ROS中测试机器人里程计信息
|
Web App开发 测试技术 API
Postman软件基本用法:浏览器复制请求信息并导入到软件从而测试、发送请求
Postman软件基本用法:浏览器复制请求信息并导入到软件从而测试、发送请求
497 1
|
人工智能 JSON 自然语言处理
超越界限:大模型应用领域扩展,探索文本分类、文本匹配、信息抽取和性格测试等多领域应用
超越界限:大模型应用领域扩展,探索文本分类、文本匹配、信息抽取和性格测试等多领域应用
超越界限:大模型应用领域扩展,探索文本分类、文本匹配、信息抽取和性格测试等多领域应用
|
IDE 测试技术 开发工具
软件测试|如何在Pycharm中配置文件头部信息
软件测试|如何在Pycharm中配置文件头部信息
|
数据采集 安全 网络协议
探寻渗透测试之道:信息收集在网络安全中的重要性
探寻渗透测试之道:信息收集在网络安全中的重要性
668 0
|
关系型数据库 MySQL Windows
对于测试时,错误信息 Caused by: com.mysql.jdbc.PacketTooBigException: Packet for query is too large
对于测试时,错误信息 Caused by: com.mysql.jdbc.PacketTooBigException: Packet for query is too large