(转)面向Web 2.0的存储引擎设想:充分利用数据访问扭曲

原文作者:风轻扬

出处:http://wangyuanzju.blog.163.com/blog/static/1302920070245344752/

警告 此为冗长的纯技术帖,为我对于Web 2.0的存储引擎如何充分利用数据访问扭曲这一特点来提高性能的一些想法。对于不关注细节的用户可以只看以下几个要点:

  1. Web(2.0)应用的数据访问模式通常具体扭曲性,即少量数据被频繁访问,其余大量数据很少被访问
  2. 传统的数据库采用的页级缓存可以利用数据访问本地性,但对数据访问扭曲性通常与事无补
  3. 利用数据访问扭曲的第一招是使用记录缓存,其效果是使相同的内存能够缓存更多的频繁访问的记录
  4. 利用数据访问扭曲的第二招是高频记录聚簇,即将频繁访问的记录从物理上聚簇在一起,其效果是使相同的IO操作可以读入更多的频繁访问的记录
  5. 记录缓存已经在一些产品中使用,但应用不多还不清楚实际效果如何,高频记录聚簇是我瞎想的,并未在知名数据库产品中使用

正文开始
在上一篇介绍Falcon体系架构的文章中我们可以看到在数据库业界有着20年实践经验的Jim Starkey在其(号称)面向Web尤其是Web 2.0应用而设计的MySQL新型事务型存储引擎Falcon时,采取了记录缓存为主、页面缓存为辅的架构。在我看来,这一架构能够充分发挥作用的前提是数据访问要具有扭曲性,同时数据访问扭曲也是大部分Web 2.0应用的特点,但我认为这一架构只是存储引擎面向Web 2.0中数据访问扭曲这一特点进行优化而走出的第一步,还可以走得更远,获取更好的效果。
首先回顾一下传统数据库所采用的页面缓存机制。由于对数据库早期的发展历史缺乏研究,我不清楚当初为什么这一缓存机制会获得流行,不过据我看来至少有以下三方面的原因:1)软件与磁盘数据交互的单位天然的是适合以块为单位进行的,因为读取一个块所用的时间也读取块中一个字节所用的时间几乎没什么差别;2)按页面为单位组织数据易于实现;3)数据访问本地性的影响。
这其中起决定作用的可能是数据访问的本地性。本地性可以是每个计算机系的学生都清楚不过的一件事,也确实存在于大部分程序的数据访问模式中,包括对数据库中数据的访问。由于本地性的影响,以页为单位缓存数据绝对是有益的,因为最近访问的记录附近的记录也很可能会被访问到。
然而,对于Web(2.0)应用而言,数据访问的扭曲性可能会比数据访问的本地性更为显著,至少是同样显著。所谓数据访问扭曲性是指同等类型的各个记录被访问的频率与均匀概率相比相去甚远。如Blog应用中一般只有少量热门Blogger的文章(如新浪博客的老徐)被频繁访问,其它用户的访问则很少会被访问不,另一个例子是对所有用户来说,一般新文章和少量热门访问被访问的概率都远大于其余文章。
有时可以通过一些数据组织手段来实现特定的数据本地性,从而利用数据访问扭曲性带来的好处。如对于上述的第一个例子,可以使用索引组织表技术,按用户聚焦文章内容。但这一方法会带来其它的问题,如发表文章时的插入点很分散,从而导致B+树的随机访问的过多的分裂。在有些情况下,数据访问扭曲性和数据本地性之间是不可转化的,如对于上述一个用户中的某篇访问被频繁访问的情况(修改应用,将经常会被访问的文章拷贝一份放到一个较小的表中可能是一个方法)。
现在再来说Falcon中的记录缓存,记录缓存的作用在于可以将所有的内存都用来保存那些真正会被访问到的记录。当频繁访问的记录能够完全或基本上被缓存在内存中时,这一方案是完美的。但若频繁访问的记录过多,即使使用了记录缓存机制也远远不够缓存所有这些记录时,记录缓存机制并不能有效的降低系统IO。记录缓存提高的内存的利用率,相对于页面缓存可能可以将缓存失配率从80%下降到70%,但对IO来说只相当于下降了1/8。
解决这一问题的手段,我个人认为一个比较有效的是对数据进行持续不断的动态重组,将频繁访问的记录聚簇到一起,暂时称之为高频记录聚簇吧。这一手段的有效性可以通过与记录缓存的对比体现,记录缓存的作用是使同样的内存可以用来缓存更多的频繁访问的记录,而高频记录聚簇的作用将是使同样的IO操作可以用来读入更多的频繁访问的记录,从而实现减少系统 IO(主要是读IO)的目的。
实现高频记录聚簇的主要困难在于需要移动记录的存储位置。在传统的数据库实现中,记录的位置由物理 rowid表示,物理rowid的一般构成为记录所在页号和页内的槽号,索引中需要记录rowid并据此来找到记录。若在跨页面移动记录,则要修改所有索引中的rowid,是一个代价相当大的的操作,因此在实际的数据库中,除了一些人工命令对记录进行重组外,都不会跨页面移动记录。而且重组的代码非常大,重组过程中表一般几乎不可用。
要实现自由的跨页移动记录,就必须实现逻辑rowid。将逻辑rowid映射到物理rowid的操作要求要尽可能高效的完成,因此基本上需要将这一映射表完全缓存中内存中。这在很长一段时间里是不现实的,但在内存容量日益增加的今天,这一方法日益值得一试,即使是使用最最简单的数组映射法。有一些简单的方法就可以大幅度减小这一映射表的大小,比如说将数据组织成页组,记录只在组内移动就可以减少映射表的大小。如每条记录一个字节就可以将记录存储位置映射到包含256页的页组内的任意一个页,则进行组内重组,在频繁访问的记录占1/256或以上时就可以实现很好的聚簇效果。
据我的经验,很多Web应用中的记录大小都会有200个字节左右,这样“逻辑rowid->物理rowid”映射表占用的空间大概是数据量的1%。对于一台4G内存的服务器,若使用500M作为存储映射表即可以处理100G的数据。对于操作频繁的Web应用数据库来说已经是相当令人满意的了。当然,对于某些记录比较短的表这一技术并不适用。
总之,对于数据访问扭曲普遍的Web(2.0)应用,记录缓存技术和高频记录聚簇技术我个人认为有望成为提高存储引擎性能的有效手段。前一个技术已经在Falcon中实现了(一些Java实现的DBMS中也有采用这一技术的,但应用不广),效果我们即将看到,后一个技术是我胡乱想的,不知道有没人真的在做这方面的工作?
关于高频记录聚簇的实现方法,使用逻辑rowid和自动重组只是其中一种方式。其它的方法应该还是很多的,对于可用性要求不太高的应用,定期使用外部程序进行人为的聚簇重组,更新所有索引也不是不可行。但不管用什么方式实现,将访问频率高的记录聚簇在一起我想会是充分利用数据访问扭曲来提高数据库性能的必要条件。

 

posted @ 2011-10-28 15:18  wwh  阅读(154)  评论(0编辑  收藏  举报