结论1:
- xmax和Infomask匹配使用,可能保存修改者的xid,也可能保存读或写行锁的xid。
- 如果保存读写行锁的xid,只要加过行锁,就算释放了xmax也会一直记录xid。
- 如果保存读行锁的xid,如果有多个事务加行锁,xmax会保存multixact数组的位置,具体信息存在multixact结构中(SLRU页面同CLOG)。
结论2:
注意到PG使用xmax记录行锁的事务ID,那么判断行锁还有没有就依赖xid的状态了,也就是CLOG。
在vacuum freeze的时候如果看到xmax上记录的事务ID过旧,也需要freeze的:
- vacuum_multixact_freeze_min_age 同 autovacuum_freeze_max_age : 五千万开始正常vacuum freeze,用最新xid-五千万=Limit,扫描页面如果有脏数据顺便freeze。
- autovacuum_multixact_freeze_max_age 同 autovacuum_multixact_freeze_max_age : 两亿开始eager freeze取oldxmin为Limit,用Limit来freeze所有页面。
正常保存
代码语言:javascript复制create table m7(id int primary key, info text);
insert into m7 select generate_series(1,10), md5(random()::text);
select 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 from heap_page_items(get_raw_page('m7',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 | 8128 | 1 | 61 | 1477538885 | 0 | 0 | (0,1) | 2 | 2050 | 24 | |
2 | 8064 | 1 | 61 | 1477538885 | 0 | 0 | (0,2) | 2 | 2050 | 24 | |
3 | 8000 | 1 | 61 | 1477538885 | 0 | 0 | (0,3) | 2 | 2050 | 24 | |
4 | 7936 | 1 | 61 | 1477538885 | 0 | 0 | (0,4) | 2 | 2050 | 24 | |
5 | 7872 | 1 | 61 | 1477538885 | 0 | 0 | (0,5) | 2 | 2050 | 24 | |
6 | 7808 | 1 | 61 | 1477538885 | 0 | 0 | (0,6) | 2 | 2050 | 24 | |
7 | 7744 | 1 | 61 | 1477538885 | 0 | 0 | (0,7) | 2 | 2050 | 24 | |
8 | 7680 | 1 | 61 | 1477538885 | 0 | 0 | (0,8) | 2 | 2050 | 24 | |
9 | 7616 | 1 | 61 | 1477538885 | 0 | 0 | (0,9) | 2 | 2050 | 24 | |
10 | 7552 | 1 | 61 | 1477538885 | 0 | 0 | (0,10) | 2 | 2050 | 24 | |
postgres=# select lp,lp_len,t_xmin,t_xmax,t_ctid,t_infomask2,t_infomask,t_hoff from heap_page_items(get_raw_page('m7',0));
lp | lp_len | t_xmin | t_xmax | t_ctid | t_infomask2 | t_infomask | t_hoff
---- -------- ------------ ------------ -------- ------------- ------------ --------
1 | 61 | 1477538885 | 1477538886 | (0,1) | 8194 | 258 | 24
2 | 61 | 1477538885 | 0 | (0,2) | 2 | 2050 | 24
3 | 61 | 1477538885 | 0 | (0,3) | 2 | 2050 | 24
4 | 61 | 1477538885 | 0 | (0,4) | 2 | 2050 | 24
5 | 61 | 1477538885 | 0 | (0,5) | 2 | 2050 | 24
6 | 61 | 1477538885 | 0 | (0,6) | 2 | 2050 | 24
7 | 61 | 1477538885 | 0 | (0,7) | 2 | 2050 | 24
8 | 61 | 1477538885 | 0 | (0,8) | 2 | 2050 | 24
9 | 61 | 1477538885 | 0 | (0,9) | 2 | 2050 | 24
10 | 61 | 1477538885 | 0 | (0,10) | 2 | 2050 | 24
id=1删除提交
id=2删除回滚
id=3行读锁
id=4行写锁
id=5行2读锁
代码语言:javascript复制postgres=# select lp,lp_len,t_xmin,t_xmax,t_ctid,t_infomask2,t_infomask,t_hoff from heap_page_items(get_raw_page('m7',0));
lp | lp_len | t_xmin | t_xmax | t_ctid | t_infomask2 | t_infomask | t_hoff
---- -------- ------------ ------------ -------- ------------- ------------ --------
1 | 61 | 1477538885 | 1477538886 | (0,1) | 8194 | 258 | 24
2 | 61 | 1477538885 | 1477538887 | (0,2) | 8194 | 258 | 24
3 | 61 | 1477538885 | 1477538889 | (0,3) | 2 | 466 | 24
4 | 61 | 1477538885 | 1477538890 | (0,4) | 8194 | 450 | 24
5 | 61 | 1477538885 | 343 | (0,5) | 2 | 4562 | 24
6 | 61 | 1477538885 | 0 | (0,6) | 2 | 2050 | 24
7 | 61 | 1477538885 | 0 | (0,7) | 2 | 2050 | 24
8 | 61 | 1477538885 | 0 | (0,8) | 2 | 2050 | 24
9 | 61 | 1477538885 | 0 | (0,9) | 2 | 2050 | 24
10 | 61 | 1477538885 | 0 | (0,10) | 2 | 2050 | 24
t_infomask
258: HEAP_HASVARWIDTH | HEAP_XMIN_COMMITTED
466: HEAP_HASVARWIDTH | HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_LOCK_ONLY | HEAP_XMIN_COMMITTED
450: HEAP_HASVARWIDTH | HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_LOCK_ONLY | HEAP_XMIN_COMMITTED
4562: HEAP_HASVARWIDTH | HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_LOCK_ONLY | HEAP_XMIN_COMMITTED | HEAP_XMAX_IS_MULTI
2050: HEAP_HASVARWIDTH | HEAP_XMAX_INVALID
t_infomask2
8194: 0010000000000010 : HEAP_KEYS_UPDATED | 低11位保存元素数量,这里是两列
2 : 0000000000000010 : 低11位保存元素数量,这里是两列