FriendFeed如何使用MySQL存储无Schema数据(一)

背景

在FriendFeed中,我们使用MySL存储所有的数据。随着用户数量的增加,数据库也在不断增长。目前,我们的存储条目数量已经超过了2.5亿,除此之外还有大量的其他数据,包括评论和喜好以及朋友列表等。

随着数据库的增长,我们不得不一而再、再而三的处理飞速成长所带来的伸缩性问题。常见的手法,我们都有过实践,例如使用只读从数据库和memcache来提高读吞吐量,以及对数据库进行分片,从而提高写入吞吐量。然而,随着技术的不断发展,通过扩展已有功能来承载更高流量所带来的问题和增加新功能相比,已经是小巫见大巫了。

尤其是为一个拥有超过一至两千万行数据的数据库做schema变更或者增加索引时,整个数据库一次就可以被彻彻底底的锁住好几个小时。删除旧索引所花费的时间大抵相等,但如果不删除这些旧索引,性能又会受到影响,因为数据库会继续在每次INSERT过程中读写这些没有被使用的索引块,是的重要的块被替换出内存。你可以使用繁琐复杂的运维手段来绕过这些问题(比如可以在某个从库上创建新索引,再把主库和从库互换),但这些过程既笨重又容易引发错误,他们不知不觉就让我们对必须引入schema或者索引的变更的新功能退避三舍。因为大量的采取了数据库分片,所以诸如JOIN这样的MySQL关系型特性,对于我们来说几乎是形同虚设。因此,我们决定跳出RDBMS的思维方式,另辟蹊径。

用来解决其schema可变数据的存储和索引的自动创建这个问题的项目为数不少(比如CouchDB就是一例)。不过,这些项目无一被大型网站所广泛采纳,因而无法让我们对其抱有太高的信心。从我们看过或自己运行过的测试结果来看,没有一个项目在稳定性和战绩上满足我们的需求(比如可以看参考资料3列出的《CouchDB性能初探》,虽然稍微有点过时)。MySQL挺靠谱,不会损坏数据;数据复制功能也很靠谱,它的缺陷我们也了如指掌。使用MySQL存储数据,我们并不排斥,只是RDBMS的使用模式不符合我们的胃口。

经过一番深思熟虑之后,我们决定摒弃采用另外一套全新系统的做法,而是以MySQL为基础实现了一套“无schema”存储系统。本文将从高层面描述这套系统的细节。其他的大型网站如何解决类似问题,我们也颇感兴趣;并且我们也相信我们的一些设计成果能对其他开发者起到抛砖引玉的作用。

概述

我们的datastore存储无schema的属性包(如JSON对象或者Python的dictionary)。存储的实体中唯一必要的属性是id,16字节的UUID值。对于datastore来说,实体的其他部分是不透明的。要修改“schema”,只需要把新的属性保存下来就可以了。

我们给这些实体中的数据做索引的方式,就是在独立的MySQL表中储存索引。如果我们想要给每个实体中的三个属性做索引,就要建立三个MySQL表,每个表分别用做一个索引。如果我们想停止使用索引,则要在代码中停止对索引表的写入,此外还可以直接从MySQL中删除这个表。加入我们打算创建一个新索引,就得为这个索引创建一个新的MySQL表,然后异步运行一个进程用来传送索引数据,而不会干扰我们运行中的服务。

这样一来,我们最终拥有的表的数量要比之前多出许多,不过索引的增删也就变得容易很多了。我么对传送新索引的进程(被称作“清扫程序(The Cleaner)”)进行了大幅度优化,使得它能够快速充填新索引而不至于拖网站速度的后退。目前,我们可以在一天的时间内完成新属性的存储,并为之建立索引,再也不用像以前一样花费一周的时间了;此外,我们也不必再做MySQL的主从库互换或者其他令人望而生畏的运维操作了。

一条评论 发表在“FriendFeed如何使用MySQL存储无Schema数据(一)”上

  1. 开心凡人 说:

    准备新的一周最后逛一逛,下周更冷了呵呵

留下回复