第3章
MVCC

MVCC(Multi-Version Concurrency Control)是一种并发控制技术,它没有规定具体的实现方法,每个数据库在实现MVCC时都可以采用不同的工程实践,但总体思想不变,就是通过记录数据副本的方式,实现读写操作互不阻塞,由于其具有出色的并发性,因此目前主流的数据库都采用MVCC作为自己的并发控制机制。

例如,MySQL、Oracle采用了一种基于“回滚段”的方法来保存元组的历史版本(前像),如果事务更新了一条元组,它可以“原地”更新这条元组(新元组的Size需要小于等于旧元组的Size),历史元组会以Undo日志记录的形式保存到回滚段中,这样就实现了元组的原地更新(Inplace Update)。当有并发事务需要访问历史元组时,可以从回滚段中“回滚”出这条元组,如果事务异常终止,则可以利用Undo日志将数据恢复。当所有可能访问历史元组的事务全部结束后,Undo日志中的历史元组就可以被清理。由于Undo日志被集中存储到某一个回滚段,所以清理也较为便捷。

实际上,MySQL和Oracle对历史元组的访问方法也不同。MySQL会记录最新元组和历史元组的联系,每次访问时都会根据最新元组和历史元组的“时间”信息来判断哪条元组对自己是可见的。而Oracle则采用了基于页面回滚的方式,当访问者需要访问某个历史元组时,它会基于缓存页面生成一个页面副本,并利用Undo日志把这个页面副本回滚到合适的历史状态,并从页面副本中读取历史元组。

而PostgreSQL将历史元组和最新元组都保存在Heap表中,这种方式的好处是无须做回滚操作,如果一个写事务异常终止,则其他事务将无法读到这条元组。

此方法虽然可以避免事务回滚带来的消耗,但仍被广为诟病。假设一个事务不停地更新数据,那么一条元组就会产生大量的历史版本。其他事务在访问时需要查看这些元组是否满足可见性要求,这会增加读操作的时延,降低数据扫描的效率。

为了防止数据膨胀,PostgreSQL数据库采用Vacuum机制清理表中的无效元组。如果使用Vacuum FULL命令,则还会负责对所有的元组进行搬迁,避免清理页面的过程中产生大量的“空洞”。

目前PostgreSQL社区正在开发新的存储引擎——Zheap存储引擎,这种存储引擎会将元组的历史版本存储到回滚段中,在第8章会对其进行介绍,本章主要分析MVCC的实现。