昨天被别人问到关于Oracle和PG的MVCC实现机制有什么不同,这块个人大体是了解一些的。Oracle在修改数据前会把数据的旧值先写到undo表空间中以实现MVCC,而PG中的更新操作会被转换为delete+insert操作,然而这个delete并不是真正的把数据立刻删除掉,而是通过修改记录中的隐藏列来标记此行被删除,并在之后的某个时间通过后台任务来进行无效数据的清理。
那么,问题来了,对比PG和Oracle这两种不同的MVCC实现方式,到底孰优孰劣?
首先,我们看一下PG这种MVCC的好处。
1. 允许更大的并发。
2. 支持更大的update操作。
3. 在不同的进程之间最小的锁定。
怎么理解呢,其实这些是相对Oracle的MVCC来说的,正如前面说的,Oracle的MVCC实现是把修改前数据存放在独立的undo表空间中,那么它就存在几个不足之处。
- 因为undo表空间一般是有大小限制的,为了保证一致性,一个事务操作里面的所有要更改的数据都需要先写到undo,那么如果事务里面修改的数据量太大,就是导致undo表空间爆满的情况,导致事务失败。这说明,Oracle这个MVCC机制对事务的大小是有限制的,事务中update的量也是有限制。而PG则不存在这个问题,只有存储空间足够大,PG里面就能支持更大的update操作。
- 同样是因为undo表空间的限制,Oracle中事务的并发度也是有限制的,如果同时有多个大事务同时进行,一旦undo表空间撑满,就会有事务会报错。这就限制了Oracle的并发度,而PG则能够允许更大的并发。
当然了,PG的MVCC机制有好处,但它也是有坏处的。既然PG里面的更新操作是转换为 旧数据打删除标记+插入新数据
,那么自然意味着,如果数据在进行频繁的更新之后,被保留的无效的旧数据就会越多,这就会膨胀问题,这个膨胀不仅会千万磁盘空间的占用大,同时也会导致查询会越来越慢。
因此,在PG数据库里面,引入了vacuum动作,就是需要定期的自动或手动触发垃圾回收机制,以确保过期的无效数据被迟早清理,释放磁盘空间,同时保证查询效率也尽可能稳定。