PG课堂笔记-第12课MVCC机制

发布时间:2024年01月03日

mvcc机制

写新数据时,旧数据不删除,而是把新数据插入,将旧数据标记为无效,PostgreSQL就是使用的这种实现方法,新老数据存放在一起,在被清理之前,会一直占据着空间,所以会导致膨胀。
两种方法各有利弊,相对于第一种来说,PostgreSQL的MVCC实现方式优缺点如下:

优点

优点
1.无论事务进行了多少操作,事务回滚可以立即完成,Oracle中使用了回滚段,如Oracle数据库宕机时如果有很多事务正在运行,这时数据库再启动后,需要把之前的事务做回滚,当没有回滚完成时,数据行上仍然有锁的,这时业务仍然不能正常操作,如果恰好碰到要回滚一些很大的事务,情况会更坏。
2.数据可以进行很多更新,不必像Oracle和MySQL的Innodb引擎那样需要经常保证回滚段不会被用完,也不会像oracle数据库那样经常遇到“ORA-1555”错误的困扰

缺点

1.旧版本的数据需要清理。好在在v8.3中,PostgreSQL引入了自动化的autovacuum,采用多进程架构,支持多表同时操作2.旧版本的数据可能会导致查询需要扫描的数据块增多,从而导致查询变慢
3.空间持续上涨,存储没有被有效利用

事务标识

postgres=# begin;
BEGIN
postgres=*# select txid_current();
 txid_current 
--------------
 ? ? ? ?  756
(1 row)
?

特殊事务标识:

0:InvalidTransactionId,表示无效的事务ID
1:BootstrapTransactionId,表示系统表初始化时的事务ID,比任何普通的事务ID都旧。
2:FrozenTransactionId,冻结的事务ID,比任何普通的事务ID都旧。
大于2的事务ID都是普通的事务ID。

代码逻辑

/*
*TransactionIdPrecedes---isid1logically<id2?
*/
bool
TransactionIdPrecedes(TransactionIdid1,TransactionIdid2){

对比逻辑

*IfeitherIDisapermanentXIDthenwecanjustdounsigned
*comparison.Ifbotharenormal,doamodulo-2^32comparison.*/
?
int32 diff;
?
if(!TransactionIdIsNormal(id1)||!TransactionIdIsNormal(id2))
return(id1<id2);diff=(int32)(id1-id2);
return(diff<0);
?
txid可以相互比较大小。例如对于txid=100的事务,大于100的txid属于“未来”,且对于txid=100的事务而言都是不可见(invisible)的;小于100的txid属于“过去”,且对该事务可见,如图5.1(a)所示。
?
因为txid在逻辑上是无限的,而实际系统中的txid空间不足(4字节取值空间约42亿),因此PostgreSQL将txid空间视为一个环。对于某个特定的txid,其前约21亿个txid属于过去,而其后约21亿个txid属于未来。如图5.1(b)所示。
?
所谓的txid回卷问题将在5.10.1节中介绍。

image-20231228100150024

元组结构

image-20231228101133365

虽然HeapTupleHeaderData结构包含七个字段,但后续部分中只需要了解四个字段即可。
?
t_xmin保存插入此元组的事务的txid。
t_xmax保存删除或更新此元组的事务的txid。如果尚未删除或更新此元组,则t_xmax设置为0,即无效。
t_cid保存命令标识(command id, cid),cid意思是在当前事务中,执行当前命令之前执行了多少SQL命令,从零开始计数。例如,假设我们在单个事务中执行了三条INSERT命令BEGIN;INSERT;INSERT;INSERT;COMMIT;。如果第一条命令插入此元组,则该元组的t_cid会被设置为0。如果第二条命令插入此元组,则其t_cid会被设置为1,依此类推。
t_ctid保存着指向自身或新元组的元组标识符(tid)。如第1.3节中所述,tid用于标识表中的元组。在更新该元组时,其t_ctid会指向新版本的元组;否则t_ctid会指向自己。

元组的增删改查

image-20231228101516841

插入操作

插入操作的过程和结果分析:
t_xmin被设置为99,表示插入该元组的txid
t_xmax被设置为0,因为该元组还未被更新或删除过
t_cid被设置为0,因为这是该事务的第一条命令
t_ctid指向自身,被设置为(0,1),表示该元组位于0号page的第1个位置上

删除操作

删除操作的过程和结果分析
t_xmin不变,也是99,
t_xmax变成为111,因为是删除操作
t_cid被设置为0,因为这是该事务的第一条命令
t_ctid指向自身,被设置为(0,1),表示该元组位于0号page的第1个位置上
当txid=111的事务提交时,tuple_1就不再需要了,称为dead tuple.但是这个dead tuple依然还是在这个page上,随着数据库的运行,这种死元组越来越多,他们会在vacuum时最终被清理掉。

更新操作

更新操作过程和结果分析
看第一条tuple
t_xmin不变,也是99,
t_xmaix变成为100,这个是删除操作,
t_cid t_cid被设置为0,因为这是该事务的第一条命令
t_ctid指向自身,被设置为(0,2),表示该元组位于0号page的第2个位置上
接着插入一条新数据
t_xmin,也是100,
t_xmaix变成为0,因为没有删除操作
t_cid被设置为0,因为这是该事务的第一条命令
t_ctid指向自身,被设置为(0,2),表示该元组位于0号page的第2个位置上
再来看第二天tuple
Tuple_2
t_xmin 不变,表示插入该元组的txid
t_xmax 被设置为100,即删除该元组的txid
t_cid 被设置为1,因为这是该事务的第二条命令
t_ctid 指向新版本元组,被设置为(0,3),表示新元组位于0号page的第3个位置上
Tuple_3
t_xmin 被设置为100,表示插入该元组的txid
t_xmax 被设置为0,因为该元组还未被更新或删除过
t_cid 被设置为1,因为这是该事务的第二条命令

xmin,xmax 与 t_xmin,t_xmax 的区别:

?
1、xmin,xmax 最早事务和最晚事务 , select txid_current_snapshot();
使用场景:MVCC事务可见性场景用到。
2、t_xmin,t_xmax  插入改元组的txid,和删除更新改元组的txid,那请问txid是什么呢?txid也就是当前事务 ID ,通过 select txid_current();  获取
使用场景:DML 语句中用到。
?
补充一点:只读事务使用的是虚拟ID,用 select txid_current_if_assigned(); 

参考博客:https://www.cnblogs.com/flying-tiger/p/9567213.html

作者公众号

文章来源:https://blog.csdn.net/qq_26009505/article/details/135362812
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。