事务回卷

事务

PostgreSQL实际上将每一个SQL语句都作为一个事务来执行。如果我们没有发出BEGIN命令,则每个独立的语句都会被加上一个隐式的BEGIN以及(如果成功)COMMIT来包围它。一组被BEGINCOMMIT包围的语句也被称为一个事务块。

MVCC

PostgreSQL实际上将每一个SQL语句都作为一个事务来执行的。数据的删改不会修改表中原有数据,update是新插入一行数据,delete是在原数据上打标记。所以PostgreSQL不靠undo来实现MVCC。

实现原理:xmin、xmax、cmin、cmax

xmin:插入该行版本的事务ID。
xmax:删除此行时的事务ID,第一次插入时,此字段为0。如果查询出来此字段不为0,则可能是删除这行的事务还未提交,或者是删除此行的事务回滚了。
cmin:事务内部的插入类操作的命令ID,此标识是从0开始的。
cmax:事务内部的删除类操作的命令ID,如果不是删除命令,此字段为0。

PostgreSQL通过比较xmin,xmax上记录的XID的新旧,来控制事务对数据的可见性。新事务可以看见老事务提交的数据。

XID

事务id,后续统称XID
事务ID是一个32bit长的无符号数字,2^32约42亿,其总是会消耗完的,消耗完之后会重新从头开始分配。其中0,1,2三个事务是内部XID,新的XID从3开始。

内部XID

  • 0 代表无效TransactionID
  • 1 代表启动事务ID,只在初始化数据库时才会使用。比所有正常事务都旧
  • 2 代表冻结事务ID。比所有正常事务都旧

XID比较大小

1、特殊事务和普通事务的比较

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

id1,id2至少有一个事务不是普通事务,则返回(id1<id2);若为true则id1<id2,若返回false则id1>id2

eg:

若id1=10,id2=2,return(10<2)。明显10<2为假,所以10比2大,普通事务较新;

若id1=2,id2=10,return(2<10)。2<10为真,所以10比2大,还是普通事务较新

2、普通事务之间的比较

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

**通过强制数据转换,将id1-id2的结果转换为带符号的int32,再赋值给diff变量,返回diff<0。 若返回为true则id1<id2,否则id1>id2 **

由于int 32带符号,需要用最高位表示符号位,所以它能表示的整数比unsigned int 32类型少一半,int 32的数据取值范围为[-2(n-1),2(n-1)-1],即[-231,231-1]。当两个XID相减结果>2^31时,转为int 32后其实是个负数(符号位从0变成了1)。

关于比较XID大小的思考:

  • id1=100,id2=30E+100 那个事务最新?id2最新?
  • 第二圈的id1=100,第一圈id2=30E+100 那个事务最新?id1最新
  • 第二圈的id1=100,第一圈id2=101 那个事务最新?id1最新???

事务回卷

 (int32) (id1 - id2)
由于比较xid新旧的算法的限制,pg数据库中的2^32个事务中,最新和最旧的事务之差,不能超过 2^31。
如图所示。

image

事务回卷&危害

新事务不能看到之前事务创建的元组,这违反了事务的可见性。这种现象称为PG的事务ID回卷问题。
当数据库中最久和最新的事务超过2^31时,事务号会发生回卷,此时数据库会报出如下错误并且拒绝接受所有连接,必须进入单用户模式执行vacuum freeze操作。

ERROR: database is not accepting commands to avoid wraparound data loss in database “xxxx”
hint: stop the postmaster and vacuum that database in single-user mode
  • 40 亿个事务,2^32,是 Postgres 中使用的数据类型的整数上限。
  • 20 亿个事务,2^31,是 PostgreSQL 在强制关闭之前允许的上限。
  • 在达到上限之前的1000 万笔交易,将记录由倒计时组成的警告消息。
  • 在达到上限之前100 万个事务,PostgreSQL 进入 READ-ONLY 模式

防止事务回卷-事务冻结

数据库通过autovaccum进程,根据参数设定定期做vacuum_freeze操作,将老的事务id冻结(在行上打标记)防止事务回卷。有两种清理模式:“惰性模式”和“急切模式”(9.4版本会直接把XID改为2的方式进行事务冻结)

  • 惰性模式:冻结过程仅使用表对应的空闲空间映射文件中包含死元组的页面.
  • 急切模式:冻结过程会对表的整个页面进行扫描。无论该表页面中是否包含有死元组。并且在可能的情况下才会移除xact(clog)文件.

事务id冻结,三个相关参数。

1、vacuum_freeze_min_age:5000万      #vacuum_freeze 应该冻结的年龄。惰性模式触发条件
2、vacuum_freeze_table_age:15亿        #表做vacuum_freeze的触发年龄。急切模式触发条件
3、autovacuum_freeze_max_age:20亿  #强制做vacuum_freeze的最大表年龄。防止事务回卷,兜底参数,触发急切模式

急切模式会导致全表扫描,引起大量的IO。影响数据库的性能,也叫做冻结炸弹。在生产中要极力避免发生急切模式的vacuum

注:vacuum有两个功能,垃圾回收+收集统计信息。autovacuum是自动执行vacuum

PG事务的优势
1、DDL可以回滚
2、没有undo,无论执行过多少SQL,事务rollback秒回
PG事务的劣势
1、表膨胀,全表扫描开销增加
2、事务回卷
我们能做什么?

  • 1、合理设置三个vacuum_freeze参数,防止事务回卷。
  • 2、监控数据库xid剩余量。
  • 3、监控表膨胀,大表改造成分区表,减小表体积。
  • 4、考虑对大表设置不同的vacuum策略
  • 5、一定要开启autovacuum,并根据系统情况,适当提升或减小autovacuum工作效能
  • 6、紧急情况下,手动执行 vacuum freeze table_name
posted @ 2023-08-30 10:22  z_uncle  阅读(162)  评论(0编辑  收藏  举报