从对数据访问扭曲的适应性评价PostgreSQL与InnoDB

PostgreSQL   2007-01-12 20:08   阅读498   评论0  
字号:    
前两篇文章中已经通过介绍Falcon的架构和对面向Web 2.0的存储引擎的讨论引出数据访问扭曲这一话题,对于我接触更多,也是最为流行的两大开源DBMS:PostgreSQL和MySQL,自然忍不住按它来比较一把了。由于这一问题只与DBMS的存储系统有关,因此这里实际上比较的是PostgreSQL与MySQL的事务型存储引擎InnoDB。

PostgreSQL与InnoDB都采用多版本并发控制技术,但两者在存储的实现机制上仍然有以下两个重大差别:
  1. PostgreSQL用堆存储记录,而InnoDB用按主键为键值的B+树索引存储记录,即受限的索引组织表
  2. PostgreSQL公平对待一个记录的所有索引版本,存储是不加以区分,而InnoDB的数据存储主体只存储最新的版本,老版本存储在回滚段中,类似于Oracle。
这两个实现策略的不同,对PostgreSQL和InnoDB对数据访问扭曲的利用程度都有相当大的影响。

首先对比堆存储和主键聚簇存储。堆存储的优点在于在某些情况下空间利用率会比主键聚簇好,因为按主键聚簇时记录的存储位置是受限的,而堆存储时则可以利用所有的空闲空间。正是基于这一点,大部分数据库中堆存储都是缺省的存储方式。但在数据访问扭曲面前,堆存储这一完全自由选择记录存储位置的优点却成了问题。由于应用无法以任何方式控制记录的存储位置和聚簇特性,频繁访问的记录在物理上的存储连续性完全没有保障。相对来说,主键聚簇存储就使得应用对数据聚簇特性有了一些控制权,如根据一个自增的ID聚簇就可以按将数据插件和顺序进行聚簇,对于那些最近的记录访问频繁的应用(很多应用是这样的)就利用了数据访问扭曲的好处;根据userid+id(实际上id完全可以唯一标识一个记录,使用userid+id作为主键纯粹是为了改变数据聚簇性质)聚簇就可以将数据按用户进行聚簇,对于那些少量用户的记录访问频繁的应用也有利(对于更新也比较频率的应用,这一方法也会有点问题)。

PostgreSQL不加区分的存储所有版本的策略对利用数据访问扭曲也是不利的。这里先要了解一下PostgreSQL里存储多版本的策略。简化来说,PostgreSQL在每条记录的每个版本头部都会记录两个事务ID:一个是产生事务ID,一个是消亡事务ID(实际上还有两个另两个语句ID,用于实现低于可串行化的隔离级别,其作用与原理与事务ID是类似的)。PostgreSQL处理多版本的简单逻辑是这样的:一个事务更新一个记录时产生一个新版本,并设置新版本的产生事务ID和和原版本的消亡事务ID为该事务ID。新旧版本除被组织成一个链表外,其物理存储没有任何其它联系,就好像是两个记录一般。事务在读取到一个版本时,通过这两个事务ID与当前事务ID之间的关系来判断它是否应该看到这个版本。这一实现策略相当简洁(对索引的多版本存储尤其是如此),通过索引访问时更不会有对回滚段的额外访问,可以消除事务回滚的代价(在PostgreSQL中事务回滚时除将事务标识为已经回滚外,不用干什么事),也不会有Oracle中繁人的回滚段大小设置问题。

然而这一实现带来的一个问题是:数据更新不是本地(in place)进行的,即更新产生的新版本不是替换掉原版本,而是插入在别处。等将来旧版本被回收后,就会产生一个存储上的空洞,为提高存储空间利用率,就会用新数据来填补这个空洞。这就使得新插入的记录的存储位置更加随机,从而更无法利用最近的记录最常被访问这一常见的数据访问扭曲行为。对于update比较频繁的数据,PostgreSQL的这一策略是有负面引影的。

总之,从对数据访问扭曲的适用性来看,似乎InnoDB都更胜一筹。PostgreSQL存储层采用的两个策略--堆存储和数据更新的非本地性--对利用数据访问扭曲都有不良影响

当然评价一个存储引擎绝非这么简单的事情,很多其它的设计抉择甚至是实现细节都会非常大的影响一个存储引擎的效率,另外存储引擎只是DBMS的其中一个组件,而本文讨论的数据访问扭曲也只是数据库应用的一个方面而已。因此PostgreSQL与MySQL仍有各自适用的场合,即便只是用作Web 2.0应用的数据库存储。另外说明特别说明的是PostgreSQL中数据更新的非本地性理论上只有在update操作比较多时才会有影响。很多应用大部分是insert操作外加少量delete,update极少,这时完全不用考虑这一点。