事务在执行完毕后,结束的动作分为两种,1 事务提交 2 事务回滚
至于事务回滚时的问题主要也分手动终止以及事务执行过程中的异常终止。
下面的一段代码是通过异步的方式来进行事务的提交的.
下面这行代码的主要控制事务的写入,在确认信息落盘后,开启日志刷新磁盘的操作 ,同时在日志commited落入磁盘后,就变换事务日志的状态,变换事务状态首先会进行同步更新,如果不OK则进行异步的状态更新。
if
((wrote_xlog && markXidCommitted &&
synchronous_commit > SYNCHRONOUS_COMMIT_OFF)
||
forceSyncCommit || nrels >
0)
-
{
-
XLogFlush(XactLastRecEnd);
-
if
(markXidCommitted)
-
TransactionIdCommitTree(xid, nchildren, children);
-
}
-
else
-
{
if (markXidCommitted) TransactionIdAsyncCommitTree(xid, nchildren, children,
XactLastRecEnd);
-
}
对应的在操作中我们也可以通过系统提供的函数来进行相关的事务状态的跟踪,如图下,跟踪一个事务整体的过程
那么根据前两期讲的,每个事务有四种状态, inprogress , abort , commited, sub_committed, 存储一个事务的状态是需要 2个bit, 一个8K的页面可以存储 8192*8/2= 32768个事务的状态。
在针对事务中的SAVE POINT 的处理中,子事务是可追踪记录自己父事务的信息,而父事务是无法自己找到自己下面的子事务,实际的提交情况会变为。
那么一个带有子事务的事务整体更新事务状态的流程为
首先子事务需要更新自身的状态,待所有的子事务状态都更新完毕(更新为自身状态sub_committed),在更新事务的状态(committed),最后在返回子事务进行事务状态的更新(committed), 类似于prepare , committed 的形式。
提到这里上面还有一个问题,是异步提交,同步提交基本上都理解,事务在 commit 后,和事务落入磁盘是原子性的问题,并且是顺序型的,顺序型在处理中也比较好处理,如果是链表的话,直接在链表后部添加即可。
而异步提交本身在不少数据库上都有使用,异步提交最大的有利点是效率和性能,postgresql 通过 walwriter 进程来不断的将事务写入磁盘,通过异步的方式中需要注意只有写入日志后, 相关的tuple 的可见性才能被落实。
所以与同步提交相比,异步提交需要注意的地方会更多,尤其在行可见性上面,需要处理的内容和步骤就变多了。
下面这段代码本身就是异步提交中对于事务LSN号的检查,如果当前事务没有被刷新,则行不可见
if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) &&
122 BufferGetLSNAtomic(buffer) < commitLSN)
123 {
124 /* not flushed and no LSN interlock, so don't set hint */
125 return;
126 }
上图在postgresql的配置文件中,有配置为 synchronous_commit 的参数默认是ON ,也就是同步提交, 如果想异步提交可以将这个位置变为 off, 所冒的风险就是事务返回给客户committed successful 和实际上wal 日志落盘之间是有延迟的,此时如果服务器DOWN机,则会导致事务丢失。这个延迟通过 wal_writer_delay 写入来控制,根据官方手册提示,最大的丢失事务的风险为 wal_writer_delay的三倍。
下面是同步提交模式下的机器的TPS
在调整了异步提交的模式后,其他参数都不懂的情况下, 提高了150TPS ,性能提高了 21%。
所以在一些可以容忍丢失事务的场景中,但对数据库本身的性能有提升的要求的情况下,可以将事务的异步提交打开,提高性能。