前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Postgresql源码(59)事务ID取值和判断规律总结

Postgresql源码(59)事务ID取值和判断规律总结

作者头像
mingjie
发布2022-07-14 13:50:19
4100
发布2022-07-14 13:50:19
举报

xid取值规律

xid是uint32类型的,GetNewTransactionId函数中xid在ShmemVariableCache->nextXid中取值,但是ShmemVariableCache->nextXid是long int类型的。

代码语言:javascript
复制
unsigned int : 0           ~  4294967295 ( 0     ~ 2^32-1 )
int          : -2147483648 ~  2147483647 ( -2^31 ~ 2^31-1 )
long int     : -9223372036854775808 ~ 9223372036854775807 ( -2^63 ~ 2^63-1 )


GetNewTransactionId
  FullTransactionId full_xid;
  TransactionId xid;
  full_xid = ShmemVariableCache->nextXid;
  xid = XidFromFullTransactionId(full_xid);

所以xid的取值会从0到4294967295在归零再次到4294967295不停循环。而ShmemVariableCache->nextXid是一直上涨的,因为ShmemVariableCache->nextXid的范围是( -2^63 ~ 2^63-1 )。

注意ShmemVariableCache->nextXid到正数最大值9223372036854775807后在加一会溢出到负数最小值-9223372036854775808,这时强转到uint32时为0,正好又是一轮循环。

取值规律见下面实例:

代码语言:javascript
复制
ShmemVariableCache->nextXid
nextXid: 0 1 2 3 ... 4294967295 4294967296 4294967297 4294967298 ... 9223372036854775807
xid    : 0 1 2 3 ... 4294967295 0          1          2          ... 4294967295    


ShmemVariableCache->nextXid
nextXid: 9223372036854775807 -9223372036854775808 -9223372036854775807 ... 0
xid    : 4294967295          0                    1                        0

ShmemVariableCache->nextXid自加使用FullTransactionIdAdvance函数

  • 该函数从0开始增加nextXid的值,第一个if保证nextXid可以正常返回0、1、2的值
  • 在后面nextXid增加到4294967296时,会走while循环把4294967296、4294967297、4294967298的值跳过,因为这三个值转换为uint32后会变成0、1、2,正常事务ID不使用这三个值。
  • 在后面nextXid继续增加每次碰到上述情况,都会走while跳过这三个数。
代码语言:javascript
复制
static inline void
FullTransactionIdAdvance(FullTransactionId *dest)
{
	dest->value++;

	/* see FullTransactionIdAdvance() */
	if (FullTransactionIdPrecedes(*dest, FirstNormalFullTransactionId))
		return;

	while (XidFromFullTransactionId(*dest) < FirstNormalTransactionId)
		dest->value++;
}

xid大小判断规律

已TransactionIdFollows函数为例,入参是两个uint32(0 ~ 2^32-1)。

但是做减法的时候结果保存到diff是int32(-2^31 ~ 2^31-1)。

代码语言:javascript
复制
typedef uint32 TransactionId;

/*
 * 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);
}
  • 注意(id1 - id2)不管怎么减结果都是正数,因为这是两个uint32在减。这个正数结果代表这两个值的距离。

当前距离-5

代码语言:javascript
复制
id1 = 4294967290u
id2 = 4294967295u
id1 - id2 = 4294967291u
diff = (int32)(id1 - id2) = -5

id2继续增长,但是id2是uint32最大值就是4294967295u了,继续增长后溢出

代码语言:javascript
复制
id1 = 4294967290u
id2 = 10u
id1 - id2 = 4294967280u
diff = (int32)(id1 - id2) = -16

可以看到溢出后,结果仍然是负数,TransactionIdPrecedes函数的计算是正确的id1 logically < id2

**但是如果id1和id2距离过大,超过231后,例如id2从刚才的10继续增长到2147483647**,id2领先id1的距离已经超过了231:

代码语言:javascript
复制
id1 = 4294967290u
id2 = 2147483647u
id1 - id2 = 2147483643u
diff = (int32)(id1 - id2) = 2147483643

结果diff又翻转了一次变成了正数,虽然id1逻辑上应该<id2,但是这时TransactionIdPrecedes的结果已经是false了。

显然TransactionIdPrecedes函数计算错误。

总结

所以在PG现有的xid分配机制上,为了保证xid回卷后还能正确的对比大小,两个xid的距离不能超过2^31。

方便记忆:事务ID可以回卷,但最老的到最新的距离不能超过20亿,否则会发生第二次符号翻转,事务ID计算结果全部都会出错。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-07-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • xid取值规律
  • xid大小判断规律
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档