对于迅速增长的网站所遇到的问题——“用灵活的模式存储数据、即时创建新的索引”, FriendFeed 的 Bret Taylor 介绍了一种“无模式的解决方案” 。问题本身源自一些需求:需要不断增加新功能,不断更新底层的数据库结构和数据库中存储的数百万条已有记录,还要同时支持新旧功能。FriendFeed 的办法是基于 MySQL 建立一个无模式的解决方案,而不是迁移到别的技术基础上去。Bret 描述了基本问题:
尤其是有一两千万行数据的时候,每次修改模式、往数据库中添加索引都会数小时完全锁定数据库。删除旧索引也需要同样长的时间,但不删除又会影响性能,因为数据库在每次执行 INSERT 操作时都会继续读写这些不用的块,而重要的块却没有足够的内存。经过一些复杂的操作过程可以克服以上困难(比如在从机上创建新索引,然后调换从机和主机),但这些操作过程都很容易出错,也都是重量级的,因此会使我们因为害怕改变模式 / 索引而不敢增加新功能。由于我们的数据库都是严重分片的,像 JOIN 这些 MySQL 的关系型功能对我们毫无用处,所以我们决定看看 RDBMS 之外的领域。
研究了几个可行的解决方案后,他们决定基于 MySQL 的自定义一种“无模式”持久化方案,而不是彻底改换门庭。
他们的解决方案是把主要数据和这些数据的索引分离开来。“我们的数据存储储存了无模式的属性包……我们在单独的 MySQL 表中存储索引,从而在这些实体中索引数据。”这是以容量来换效率。
结果我们比以前多了很多的表,但添加和删除索引却很容易。我们大力优化了填充新索引的进程(我们称之为“Cleaner”),以便该进程能快速填充新索引,而不会让站点中断。
分离数据和索引引起了一致性和原子性问题。他们没有建立严格的事务规则,而是把数据库表推到最简,索引只用来引用,发生实际的数据库读操作的时候才施加数据过滤。他们改进了持续更新表的自动化进程,让这个“Cleaner”进程不停地对优先级高的被更新实体进行更新和索引修正。尽管可能出现不一致,但消除不一致的时间平均不到两秒钟。
Bret 用平均页面延迟这一指标描述了以下走向。
- 整体来说——尽管有增加的趋势,但还是有显著的减少。
- 过去二十四小时——即使在高峰时段也保持稳定。
- 前一周——明显减少。
Bret 的帖子有很多回复。有一种观点认为“对于模式演变,现代的RDBMS 不像MySQL 局限那么大”,这种观点忽略了选择背后的成本问题。其它读者则回复了更多种不同的解决办法。
有意思的是,并没有人指出FriendFeed 的解决方案与古老的ISAM 技术(Indexed Sequential Access Method,索引顺序存取法)之间的相似性。ISAM 用的是同样的基本架构——分离数据和索引,同时在数据发生变化时自动更新索引。
查看英文原文: FriendFeed Implements Schema-less Storage Atop MySQL
评论