上文

Cache Money虽然解决了数据的读取性能瓶颈;但开发大网站数据库面临的问题远不至读压力。

首先是容量。

上千万/亿的数据量并不罕见,单一物理数据库服务器即便单纯承担写压力也会是瓶颈。更何况Cache Money仅仅是在理想状况下才可以做到数据库0读。缓存服务器更新,新增查询,复杂查询等等都还会造成读压力。

比较常见的做法是采用分表,也就是所谓的Sharding,把数据按照一定的规则,分别存储至多台数据库服务器上去。

其次是变动。

业务需求是不可预测的;无论一开始数据库表结构定义得如何完备,总会有新需求出来,需要对表结构做调整才可以实现。

数据量过了百万之后,每次对生产服务器做alter table/create index等调整都是痛苦的经历。

针对容量与变动这两个问题,FriendFeed提出的schema-less database design给出了一个相当漂亮的解决方案。

强烈推荐阅读FriendFeed的原文。

FriendFeed的方案大致是这样:

  • 只有一种表结构,只有两个列:id + blob/binary(max)
  • id本身是UUID,这本身可以很容易做sharding
  • blob可以反序列化为任意结构
  • 查询通过另外建表实现,比方说users表的blob列反序列化出来的结构中包含一个age的int属性;要查询select * from users where age = 18; 那么就另外建表如user_age,仅包括两列id / age;先查询此表获得id,再查询原本的users表获得完整数据
  • 索引表可以异步建立,而且,建立的时候它都是跟查询相关,可以根据查询条件做sharding;如上面所的age。

FriendFeed的方案相当聪明,数据本身结构及其简单,sharding很容易做。写/读压力一下子就分布出去。

blob列用于序列化(数据甚至是先zip过再存,CPU强劲,磁盘IO是瓶颈),所以结构可以随时变化;只需要保证序列化算法可以兼容不同版本即可。

而灵活的序列化,恰恰是Facebook Thrift所解决的!

(还记得一开始使用Memcached做object cache时采用了Thrift做序列化么?)

先不考虑Sharding分布方案,在MoSonic中将各个类定义为类似下面的结构:

  • id(int)
  • properties(blob)
    • user_name(varchar)
    • age(int)
    • ...
  • ...
使用时可以直接这样:User.FetchById(XXX).properties.user_name。
 
因为一开始已经把Thrift序列化代码生成做到SubSonic模板时,这里要给数据多增加一层结构也并非难事;有点水到渠成的感觉。

以后要修改数据结构,直接改Thrift的定义文件,然后重新生成代码就成。properties列中存的数据可能跟最新的结构不一直,但Thrift并不要求严格的匹配(BinaryFormatter则不然),它会自动忽略那些不符合的列;而一但Object被重新存入,数据就又会被重新序列化完整。

======================

FriendFeed的分布式方案要求表主建是uuid,而cache money却要求所有表必须要有自增的ID主健。

这其实不是冲突,把database_name + table_name + id看成一个uuid即可。

而FriendFeed的分布式索引,跟cache money中Vector Cache有异曲同工之妙。

都是根据查询条件做处理/sharding。

之前为MoSonic添加Vector Cache,已经需要判断查询的表名/查询条件;符合即查询缓存;这里套用FriendFeed的方案则变成,符合即查询分布式索引!

执行select id from users where age=18 limit order by id desc 0,10时

逻辑变成这样:

 

  1. 检查Vector Cache,存在便返回
  2. 检查分布式索引表规则,获得新的数据库连接字符串
  3. 执行查询
  4. 写入Vector Cache

 

插入数据时,之前仅是更新Vector Cache,现在则需多一步去插入索引表。

实际运行中,因为是先插入数据表,同步更新Vector Cache,后续的插叙已经会命中缓存;索引表的更新实质变成是备份,可以异步插入。

Thrift / Cache Money / Schema-less Database Design实际上是三个不同团队为了解决不同方面的技术问题而做出的方案,但糅合进MoSonic中时,我感到的不是冲突,而更多的是一种不谋而合的美妙。

下篇会继续讲更多一些细节。