暂无图片
暂无图片
2
暂无图片
暂无图片
暂无图片

PostgreSQL 中 xid 与 txid

5025

当我们开始接触PostgreSQL的时候,我们会了解到PostgreSQL事务id是通过32位无符号数来表示的:2^32 = 4294967296,当事务id超过40亿以后就会有溢出的风险,为了防止事务回卷,PostgreSQL通过vacuum freeze的方式来循环利用这些事务号,如果vacuum freeze 不及时或者遇到故障,数据库会给相关报警提示,甚至关闭数据库,可以参考以下链接模拟复现:https://www.modb.pro/db/31736,之所以没有引入64位无符号数来解决这个问题,是因为存储64位的元组头部信息(xmin,xmax)需要扩大存储的字节(2bit),而社区希望通过借助缓存的方法,只使用一个bit就能达到64位的效果。

事务号比较

/*
 * TransactionIdPrecedes --- is id1 logically < id2?
 */
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);
}

事务号是通过(int32) (id1 - id2)来对比的,当事务号发生了回卷,即id2的值比id1的值小,但是由于(id1 - id2)大于2^31, 经过int32后转换成一个负数,但如果id1 和 id2 都是回卷前的数值且大于2^31, 同样会出现问题,所以PostgreSQL要保证两个有效的事务id不能大于2^31。

xid与txid 查询转换

再运维过程中我们会关注数据库当前的事务号,需要通过txid_current()函数来查看,我们会发现当前的事务号已经远远大于2^32,如图所示

image.png

这里我们注意到,我们查询事务id用的是txid而不是xid,这是因为txid是在全局的视角上做的持久化,可以被其他机器使用,txid是不会被循环的64位整形数值,而xid是一个32位的类型,xmin和xmax都是xid类型,xid类型的数值是会被freeze循环使用的,只有最顶层的xid才会通过epoch来实现txid和xid转换。通过xid类型的数值可以得到事务的结束时间,而通过txid可以得到事务的状态。

/*  
 *      Export internal transaction IDs to user level.  
 *  
 * Note that only top-level transaction IDs are ever converted to TXID.  
 * This is important because TXIDs frequently persist beyond the global  
 * xmin horizon, or may even be shipped to other machines, so we cannot  
 * rely on being able to correlate subtransaction IDs with their parents  
 * via functions such as SubTransGetTopmostTransaction().  
 *  
/* txid will be signed int8 in database, so must limit to 63 bits */  
#define MAX_TXID   UINT64CONST(0x7FFFFFFFFFFFFFFF)  
  
/*  
 * do a TransactionId -> txid conversion for an XID near the given epoch  
 */  
static txid  
convert_xid(TransactionId xid, const TxidEpoch *state)  
{  
        uint64          epoch;  
  
        /* return special xid's as-is */  
        if (!TransactionIdIsNormal(xid))  
                return (txid) xid;  
  
        /* xid can be on either side when near wrap-around */  
        epoch = (uint64) state->epoch;  
        if (xid > state->last_xid &&  
                TransactionIdPrecedes(xid, state->last_xid))  
                epoch--;  
        else if (xid < state->last_xid &&  
                         TransactionIdFollows(xid, state->last_xid))  
                epoch++;  
  
        return (epoch << 32) | xid;  
}  

转换公式可以简化成:txid = xid + epoch * 2^32

epoch可以通过pg_controldata命令获取到,即NextXID中分号前面的数值
image.png

epoch 可以通过pg_resetwal -e 2 /data/pgdata14/ 的方式修改,修改epoch需要停止数据库实例。
image.png
xid的获取可以通过两种方式:
1、通过pg_waldump 解析wal日志
2、直接在表中查询: select xmin,xmax,* from tablename;

通过xid查事务结束时间及通过txid 查事务状态,这里以epoch=1为例
wecomtempedad5cde31116fab48e6689bd72f9a2d.png

事务ID特性

1、普通的事务号是从3开始的,0表示无效的xid;1表示初始化时创建catalog的xid;2表示冻结xid。
2、两个有效的事务号之间相差不能大约2^31。
3、由于数据库中的事物号非常宝贵,对于未开启显示事务的查询来说,是不会消耗事务号的。
wecomtemp4920aae44fc4b3f66a3884afdc095b60.png

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
1人已赞赏
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论