1. Postgresql中的多版本并发控制
1.原理
MVCC的原理是在写数据时,旧版本的数据并不删除,并发的读还能读到旧版本的数据,这样就不会出现数据不一致的问题。
实现MVCC的方法有两种:
(1)在写数据时,把旧数据移到一个单独的地方,如回滚段中,其他人在读数据时,从回滚段中把旧数据读出来。
(2)写新数据时,旧数据不删除,而是把新数据插入。
Postgresql数据库使用的就是第二种方法。而oracle数据库和MySQL数据库的innodb引擎使用的是第一种方法。
2.Postgresql中的多版本并发控制
上面说到,Postgresql数据库是通过把旧数据留在数据文件中,新插入一条数据来实现多版本功能的。而且为了实现此功能,每张表都添加了四个系统字段xmin、xmax、cmin、cmax。当两个事务同时访问记录时,通过参考xmin、xmax的标记可判断记录的版本,然后根据版本号与自己当前事务标识进行比较,确定自己的数据权限。当删除数据时,需要记住记录并没有从数据块中删除,空间也没有立即释放。
Postgresql中多版本实现首先要解决的是旧数据的空间释放问题。Postgresql通过运行vacuum进程来回收之前的存储空间,默认Postgresql数据库中的autovacuum是打开的,也就是说当一个表的更新达到一定数量时,autovacuum自动回收空间。当然也可以关闭autovacuum,然后在业务低峰期手动运行vacuum来回收空间。
在Postgresql中,若一个事务失败,在数据文件中这个事务产生的数据并不会在事务回滚时被清理掉。为什么不在事务提交时将数据标记为有效的,事务回滚后表示为无效的?这是出于效率的考虑,若事务提交或回滚时再次标记了数据,这些数据就有可能会被刷新到磁盘上,而再次标记会导致另一个IO消耗,降低了数据库性能。而Postgresql通过记录事务的状态来实现,因为数据行上记录了xmin和xmax,所以只需了解了这两个系统字段对应的事务是成功提交还是回滚就可以知道这些数据行是否有效。
Postgresql数据库中事务ID缩写为xid,是一个32字节的数字,有以下三种特殊的事务ID是给系统内部使用的,它们有这特殊的含义:
(1)InvalidTransactionId = 0,表示是无效的事务ID。
(2)BootstrapTransactionId = 1,表示系统表初始化时的事务ID。
(3)FrozenTransactionId = 2,表示冻结的事务ID。
所以数据库系统第一个正常的事务ID是从3开始的,然后不停的递增,达到最大值后,再从3开始。事务ID为0、1、2的始终保留。
3.Postgresql多版本的优劣分析
Oracle数据库和MySQL数据库的innodb引擎都实现了多版本的功能,但它们与Postgresql数据库的实现方式不同。在两个数据库中旧版本的数据并不记录在原先的数据块中,而是记录在回滚段中。如果要读取旧版本的数据,需要根据回滚段的数据重构旧版本的数据。
Postgresql的多版本机制与Java虚拟机的垃圾回收机制类似。事务提交前,只需访问原来的数据,提交后,系统更新元祖的存储标识,直到vacuum进程收回为止。
因此Postgresql多版本的优势在于:
(1)事务回滚可以立即完成,无论事务进行了多少操作。
(2)数据可以进行很多更新,不必如Oracle或MySQL那样需要经常保证回滚段不会被用完,导致无妨读取旧数据而报错。
而Postgresql的劣势在于:
(1)旧版本的数据需要清理,Postgresql清理旧版本的命令称之为vacuum。
(2)旧版本的数据会导致查询更慢,以为旧版本数据存放是在磁盘中,查询就会导致需要扫描磁盘数据块。