这篇文章主要介绍“PostgreSQL中的Multi Version Heap Tuple分析”,在日常操作中,相信很多人在PostgreSQL中的Multi Version Heap Tuple分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PostgreSQL中的Multi Version Heap Tuple分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
Concurrency Control并发控制是一种机制,在并发进行多个事务时维护一致性(Consistency)和隔离性(Isolation),一致性和隔离性是数据库事务ACID(Atomicity, Consistency, Isolation, Durability) 属性中的C和I。
多版本并发控制(MVCC)是广泛使用的并发控制技术,其主要优势是读不会阻塞写,而写也不会阻塞读。MVCC有很多种变体,PostgreSQL使用一种称为快照隔离Snapshot Isolation (SI)的MVCC变体实现并发控制。
在MVCC中,每个DML操作创建一个数据(包括Index)的新版本,同时保留之前的旧版本。当事务读取数据时,选择其中一个“正确”的版本,以确保各个事务之间的隔离。
为了更好的说明Heap Tuple的存储结构,有必要先简要说明Tuple的隐藏列以及相关的标记.
隐藏列
testdb=# select attname, attnum, atttypid::regtype, attisdropped::text from pg_attribute where attrelid=34374; attname | attnum | atttypid | attisdropped ----------+--------+-------------------+-------------- tableoid | -7 | oid | false cmax | -6 | cid | false xmax | -5 | xid | false cmin | -4 | cid | false xmin | -3 | xid | false ctid | -1 | tid | false c1 | 1 | integer | false c2 | 2 | character varying | false c3 | 3 | character varying | false (9 rows)
tableoid-数据表OID
cmax-删除该tuple的事务内部命令ID
xmax-删除该tuple的事务ID
cmin-插入该tuple的事务内部命令ID
xmin-插入该tuple的事务ID
ctid-heap tuple的ID
infomask标记
主要的标记包括t_infomask2和t_infomask.
t_infomask2
取值和说明如下
/* * information stored in t_infomask2: */ #define HEAP_NATTS_MASK 0x07FF /* 11 bits for number of attributes */ //低11位为属性个数 /* bits 0x1800 are available */ #define HEAP_KEYS_UPDATED 0x2000 /* tuple was updated and key cols * modified, or tuple deleted */ #define HEAP_HOT_UPDATED 0x4000 /* tuple was HOT-updated */ #define HEAP_ONLY_TUPLE 0x8000 /* this is heap-only tuple */ #define HEAP2_XACT_MASK 0xE000 /* visibility-related bits */ //把十六进制值转换为二进制显示 11111111111 #define HEAP_NATTS_MASK 0x07FF 10000000000000 #define HEAP_KEYS_UPDATED 0x2000 100000000000000 #define HEAP_HOT_UPDATED 0x4000 1000000000000000 #define HEAP_ONLY_TUPLE 0x8000 1110000000000000 #define HEAP2_XACT_MASK 0xE000 1111111111111110 #define SpecTokenOffsetNumber 0xfffe
t_infomask
取值和说明如下
//t_infomask说明 1 #define HEAP_HASNULL 0x0001 /* has null attribute(s) */ 10 #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ 100 #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ 1000 #define HEAP_HASOID 0x0008 /* has an object-id field */ 10000 #define HEAP_XMAX_KEYSHR_LOCK 0x0010 /* xmax is a key-shared locker */ 100000 #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ 1000000 #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ 10000000 #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) 100000000 #define HEAP_XMIN_COMMITTED 0x0100 /* t_xmin committed */ 1000000000 #define HEAP_XMIN_INVALID 0x0200 /* t_xmin invalid/aborted */ #define HEAP_XMIN_FROZEN (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID) 10000000000 #define HEAP_XMAX_COMMITTED 0x0400 /* t_xmax committed */ 100000000000 #define HEAP_XMAX_INVALID 0x0800 /* t_xmax invalid/aborted */ 1000000000000 #define HEAP_XMAX_IS_MULTI 0x1000 /* t_xmax is a MultiXactId */ 10000000000000 #define HEAP_UPDATED 0x2000 /* this is UPDATEd version of row */ 100000000000000 #define HEAP_MOVED_OFF 0x4000 /* moved to another place by pre-9.0 * VACUUM FULL; kept for binary * upgrade support */ 1000000000000000 #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) 1111111111110000 #define HEAP_XACT_MASK 0xFFF0 /* visibility-related bits */
创建数据表,插入数据
testdb=# drop table if exists t_mvcc1; DROP TABLE testdb=# create table t_mvcc1 (c1 int,c2 varchar(40)); CREATE TABLE testdb=# testdb=# insert into t_mvcc1 values(1,'C2-1'); INSERT 0 1 testdb=# insert into t_mvcc1 values(2,'C2-2'); INSERT 0 1 testdb=#
通过pageinspect插件查看page中的内容
testdb=# select lp,lp_off,lp_flags,t_xmin,t_xmax,t_field3 as t_cid,t_ctid,t_infomask2,t_infomask from heap_page_items(get_raw_page('t_mvcc1',0)); lp | lp_off | lp_flags | t_xmin | t_xmax | t_cid | t_ctid | t_infomask2 | t_infomask ----+--------+----------+--------+--------+-------+--------+-------------+------------ 1 | 8152 | 1 | 2300 | 0 | 0 | (0,1) | 2 | 2050 2 | 8112 | 1 | 2301 | 0 | 0 | (0,2) | 2 | 2050 (2 rows)
其中lp为Line Pointer(ItemID,行指针),t_xmin(分别是2300&2301)为插入数据的事务ID,t_xmax为0(Invalid事务号),t_cid是命令编号,t_ctid是heap tuple ID,详细解释请参见参考资料.
t_infomask2为0x0002,说明有2个字段(低11位为属性的个数);
t_infomask为2050,即0x0802,标记存在可变长属性(HEAP_HASVARWIDTH)/XMAX无效(HEAP_XMAX_INVALID)
更新数据(提交事务)
testdb=# testdb=# begin; BEGIN testdb=# testdb=# update t_mvcc1 set c2='C2#1' where c1 = 1; UPDATE 1 testdb=# update t_mvcc1 set c2='C2#2' where c1 = 2; UPDATE 1 testdb=# testdb=# commit; COMMIT
通过pageinspect插件查看page中的内容
testdb=# select lp,lp_off,lp_flags,t_xmin,t_xmax,t_field3 as t_cid,t_ctid,t_infomask2,t_infomask from heap_page_items(get_raw_page('t_mvcc1',0)); lp | lp_off | lp_flags | t_xmin | t_xmax | t_cid | t_ctid | t_infomask2 | t_infomask ----+--------+----------+--------+--------+-------+--------+-------------+------------ 1 | 8152 | 1 | 2300 | 2302 | 0 | (0,3) | 16386 | 258 2 | 8112 | 1 | 2301 | 2302 | 1 | (0,4) | 16386 | 258 3 | 8072 | 1 | 2302 | 0 | 0 | (0,3) | 32770 | 10242 4 | 8032 | 1 | 2302 | 0 | 1 | (0,4) | 32770 | 10242 (4 rows)
可以看到原数据仍存在,但t_xmax值为2302,表示这两行已被更新,同时t_ctid指向新的heap tuple.
1/2号tuple的t_infomask2是16386即0x4002 -> HEAP_HOT_UPDATED
t_infomask是258,即0x0102 -> HEAP_XMIN_COMMITTED | HEAP_HASVARWIDTH
3/4号tuple的t_infomask2是32770,即0x8002 -> HEAP_ONLY_TUPLE
t_infomask是10242,即0x2802 -> HEAP_UPDATED | HEAP_XMAX_INVALID | HEAP_HASVARWIDTH
更新数据(回滚事务)
testdb=# begin; BEGIN testdb=# testdb=# update t_mvcc1 set c2='C2_1' where c1 = 1; UPDATE 1 testdb=# update t_mvcc1 set c2='C2_2' where c1 = 2; UPDATE 1 testdb=# testdb=# rollback; ROLLBACK testdb=# select cmin,cmax,xmin,xmax,ctid,c1,c2 from t_mvcc1; cmin | cmax | xmin | xmax | ctid | c1 | c2 ------+------+------+------+-------+----+------ 0 | 0 | 2302 | 2303 | (0,3) | 1 | C2#1 1 | 1 | 2302 | 2303 | (0,4) | 2 | C2#2 (2 rows)
通过pageinspect插件查看page中的内容
testdb=# select lp,lp_off,lp_flags,t_xmin,t_xmax,t_field3 as t_cid,t_ctid,t_infomask2,t_infomask from heap_page_items(get_raw_page('t_mvcc1',0)); lp | lp_off | lp_flags | t_xmin | t_xmax | t_cid | t_ctid | t_infomask2 | t_infomask ----+--------+----------+--------+--------+-------+--------+-------------+------------ 1 | 8152 | 1 | 2300 | 2302 | 0 | (0,3) | 16386 | 1282 2 | 8112 | 1 | 2301 | 2302 | 1 | (0,4) | 16386 | 1282 3 | 8072 | 1 | 2302 | 2303 | 0 | (0,5) | 49154 | 8450 4 | 8032 | 1 | 2302 | 2303 | 1 | (0,6) | 49154 | 8450 5 | 7992 | 1 | 2303 | 0 | 0 | (0,5) | 32770 | 10242 6 | 7952 | 1 | 2303 | 0 | 1 | (0,6) | 32770 | 10242 (6 rows)
3/4号(lp=3/4)tuple被更新,t_xmax设置为更新事务的ID,但事务rollback(PG通过clog记录事务状态,clog后续再行讨论).
t_infomask2=49154,即0xC002
t_infomask=8450,即0x2102 -> HEAP_UPDATED | HEAP_XMIN_COMMITTED | HEAP_HASVARWIDTH
5/6号tuple是新生成的更新记录,但事务rollback.
t_infomask2=32770,即0x8002 -> HEAP_ONLY_TUPLE
t_infomask=10242,即0x2802 -> HEAP_UPDATED | HEAP_XMAX_INVALID | HEAP_HASVARWIDTH
删除数据(提交事务)
testdb=# begin; BEGIN testdb=# testdb=# delete from t_mvcc1 where c1 = 1; DELETE 1 testdb=# testdb=# commit; COMMIT testdb=# testdb=# select cmin,cmax,xmin,xmax,ctid,c1,c2 from t_mvcc1; cmin | cmax | xmin | xmax | ctid | c1 | c2 ------+------+------+------+-------+----+------ 1 | 1 | 2302 | 2303 | (0,4) | 2 | C2#2 (1 row)
通过pageinspect插件查看page中的内容
testdb=# select lp,lp_off,lp_flags,t_xmin,t_xmax,t_field3 as t_cid,t_ctid,t_infomask2,t_infomask from heap_page_items(get_raw_page('t_mvcc1',0)); lp | lp_off | lp_flags | t_xmin | t_xmax | t_cid | t_ctid | t_infomask2 | t_infomask ----+--------+----------+--------+--------+-------+--------+-------------+------------ 1 | 8152 | 1 | 2300 | 2302 | 0 | (0,3) | 16386 | 1282 2 | 8112 | 1 | 2301 | 2302 | 1 | (0,4) | 16386 | 1282 3 | 8072 | 1 | 2302 | 2304 | 0 | (0,3) | 40962 | 9474 4 | 8032 | 1 | 2302 | 2303 | 1 | (0,6) | 49154 | 10498 5 | 7992 | 1 | 2303 | 0 | 0 | (0,5) | 32770 | 10754 6 | 7952 | 1 | 2303 | 0 | 1 | (0,6) | 32770 | 10754 (6 rows)
3号(lp=3) tuple被删除,t_xmax修改为2304,t_ctid修改为(0,3).
t_infomask2=40962,即0xA002
t_infomask=9474,即0x2502 -> HEAP_UPDATED | HEAP_XMAX_COMMITTED | HEAP_XMIN_COMMITTED | HEAP_HASVARWIDTH
删除数据(回滚事务)
testdb=# begin; BEGIN testdb=# testdb=# delete from t_mvcc1 where c1 = 2; DELETE 1 testdb=# testdb=# rollback; ROLLBACK testdb=# testdb=# select cmin,cmax,xmin,xmax,ctid,c1,c2 from t_mvcc1; cmin | cmax | xmin | xmax | ctid | c1 | c2 ------+------+------+------+-------+----+------ 0 | 0 | 2302 | 2305 | (0,4) | 2 | C2#2 (1 row)
xmax修改为事务号2305(原为2303).
通过pageinspect插件查看page中的内容
testdb=# select lp,lp_off,lp_flags,t_xmin,t_xmax,t_field3 as t_cid,t_ctid,t_infomask2,t_infomask from heap_page_items(get_raw_page('t_mvcc1',0)); lp | lp_off | lp_flags | t_xmin | t_xmax | t_cid | t_ctid | t_infomask2 | t_infomask ----+--------+----------+--------+--------+-------+--------+-------------+------------ 1 | 8152 | 1 | 2300 | 2302 | 0 | (0,3) | 16386 | 1282 2 | 8112 | 1 | 2301 | 2302 | 1 | (0,4) | 16386 | 1282 3 | 8072 | 1 | 2302 | 2304 | 0 | (0,3) | 40962 | 9474 4 | 8032 | 1 | 2302 | 2305 | 0 | (0,4) | 40962 | 10498 5 | 7992 | 1 | 2303 | 0 | 0 | (0,5) | 32770 | 10754 6 | 7952 | 1 | 2303 | 0 | 1 | (0,6) | 32770 | 10754 (6 rows)
删除4号(lp=4) tuple,但事务回滚,t_max修改为2305.
t_infomask2=40962,即0xA002
t_infomask=10498,即0x2902 -> HEAP_UPDATED | HEAP_XMAX_INVALID | HEAP_XMIN_COMMITTED | HEAP_HASVARWIDTH
到此,关于“PostgreSQL中的Multi Version Heap Tuple分析”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。