【PG内核】事务ID冷冻简述

原创
2019/01/22 11:29
阅读数 328

数据库代码中事务ID的类型TransactionId定义为:typedef uint32 TransactionId。因此事务ID最大值为2^32-1=4294967295。

事务ID是需要循环使用的,为了做到这一点,数据库在做vacuum时将很老的事务ID冷冻,来完成旧事务ID的回收。

下面就讲解事务ID冷冻的本质:

举例说明事务ID冷冻

highgo=# insert into t1 values(1,1);
INSERT 0 1
highgo=# SELECT * FROM heap_page_items(get_raw_page('t1', 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 |       t_data       
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------+--------------------
  1 |   8160 |        1 |     32 |   1246 |      0 |        0 | (0,1)  |           2 |       2048 |     24 |        |       | \x0100000001000000
(1 row)

highgo=# vacuum ;
VACUUM
highgo=# SELECT * FROM heap_page_items(get_raw_page('t1', 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 |       t_data       
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------+--------------------
  1 |   8160 |        1 |     32 |   1246 |      0 |        0 | (0,1)  |           2 |       2304 |     24 |        |       | \x0100000001000000
(1 row)

highgo=# vacuum full;
VACUUM
highgo=# SELECT * FROM heap_page_items(get_raw_page('t1', 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 |       t_data       
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------+--------------------
  1 |   8160 |        1 |     32 |   1246 |      0 |        0 | (0,1)  |           2 |       2816 |     24 |        |       | \x0100000001000000
(1 row)

highgo=#

如上图向t1表插入一行数据之后,查询其page数据,做vacuum full后再次查询其page数据。

发现其t_infomask属性发生了变化:2816-2048=768=300(十六进制)(做vacuum后t_infomask变化没有这么大,原因后面会讲到)

在代码中有如下宏值

#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)

事务ID1026冷冻,就是将所有xmin为1026的tuple的t_infomask置为t_infomask | HEAP_XMIN_FROZEN

事务ID冰冻相关代码

做vacuum时,原则上需要遍历所有的tuple,遍历时通过heap_prepare_freeze_tuple()函数判断tuple的xmin是否需要被冰冻

如果需要被冰冻,则通过heap_execute_freeze_tuple()执行冰冻处理

注意:

代码逻辑->

当tuple的xmin小于cutoff_xid时,会判断需要做冰冻处理。

如下为做lazy vacuum时和做vacuum full时调用heap_prepare_freeze_tuple的入参情况。

可见当lazy vacuum时cutoff_xid为一个很大的值,因此当tuple的xmin大到一个阀值后,lazy vacuum才会冰冻事务ID

--lazy vacuum
Breakpoint 3, heap_prepare_freeze_tuple (
    tuple=0x7f85e91fa2f8, 
    cutoff_xid=4244968248, 
    cutoff_multi=4289967297, 
    frz=frz@entry=0x1046618, 
    totally_frozen_p=totally_frozen_p@entry=0x7fffcb3736a0 "") at heapam.c:6672
6672			if (TransactionIdPrecedes(xid, cutoff_xid))


--vacuum full
Breakpoint 3, heap_prepare_freeze_tuple (
    tuple=tuple@entry=0x10475b0, 
    cutoff_xid=952, cutoff_multi=1, 
    frz=frz@entry=0x7fffcb3734e0, 
    totally_frozen_p=totally_frozen_p@entry=0x7fffcb3734df "") at heapam.c:6672
6672			if (TransactionIdPrecedes(xid, cutoff_xid))

事务ID大小比较的玄机

代码中有很多事务ID大小比较的情况,循环使用事务ID后,如何保证事务ID大小比较是符合逻辑的呢。

bool
TransactionIdPrecedes(TransactionId id1, TransactionId id2)
{
	/*
	 * If either ID is a permanent XID then we can just do unsigned
	 * comparison.  If both are normal, do a modulo-2^32 comparison.
	 */
	int32		diff;

	if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
		return (id1 < id2);

	diff = (int32) (id1 - id2);
	return (diff < 0);
}

如上一个比较事务ID大小的函数。

将两个unsigned int 做差,然后转化为signed int.

比如:

id1 = 2^32 -1 = 4294967295

id2 = 952

那么

id1 -id2=4294966343

diff = int(id1-id2)=-953

函数返回结果id1小于id2,即4294967295小于952

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部