解读FriendFeed对MySQL的使用

来源:互联网 发布:下载手机截图软件 编辑:程序博客网 时间:2024/05/18 19:35

如何才能规避MySQL中DDL的堵塞问题?FriendFeed给出了答案:How FriendFeed uses MySQL to store schema-less data。

DDL指的是CREATE,DROP,ALTER之类的操作。当在MySQL中使用它们时,比如说添加一个索引,一个字段,此时MySQL会锁住表,如果数据量很大的话,这个锁表的时间可能会持续很久,在此期间,任何其它的写操作都不能执行。这无疑是个让人恼火的问题。

首先能想到的解决办法是把添加索引之类的DDL操作放到晚上进行。对于小网站来说,一般晚上使用网站服务的用户不多,可以选择这样的方法,但是对于一个大网站而言,网站服务必须要求全天候的可用性。这个方法只能算是权宜之计。

再一个方法是利用主从服务器绕开添加索引之类的DDL堵塞问题。常见的主从结构一般是一主多从,写操作在主服务上完成,读操作由多台从服务器共同承担。当我们想添加索引的时候,其实就是给从服务器添加索引,此时可以先让一台从服务器下线(slavestop),并按常规方式在这台从服务器上添加索引,期间网站的查询服务由其它从服务器代劳,索引添加完毕后,重新把这台从服务器上线(slavestart),周而复始,直到所有的从服务器都添加了索引。整个过程对网站服务的使用来说基本是透明的,可惜这个方法有弊端:如果DDL操作不是添加索引,而是添加字段的话,那么必须要在主服务器上进行操作,整个过程无疑要复杂很多,想要保证网站服务的平滑过度也就不是一件容易的事情了。

最后说说FriendFeed的方法:为每一个索引创建一个表!在FriendFeed里,实体按照类似下面的结构存储:

CREATE TABLE entities (
    added_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    id BINARY(16) NOT NULL,
    updated TIMESTAMP NOT NULL,
    body MEDIUMBLOB,
    UNIQUE KEY (id),
    KEY (updated)
) ENGINE=InnoDB;


需要说明的是added_id是物理上的主键(autoincrement),id是逻辑上的主键(uuid)。之所以这样做是因为uuid的不重复性对数据分布有好处,但因为InnoDB表类型会按照主键排序,此时如果使用类似uuid这样的无序串作为主键的话,那么当插入新行的时候,新行在数据文件中的位置是不确定的,这就会带来一个沉重的IO负担,而如果采用自增字段作为主键的话就不存在这个问题,因为新行始终位于数据文件的结尾。

还有一点需要说明的是body字段,此字段相当于是一个属性容器,可以把大多数实体属性都转换成二进制压缩后保存到这个字段里,实际使用的时候,当你通过id定位到实体后,需要的数据都可以从这个字段得到,就不需要再像传统做法那样和其它表进行JOIN之类的连接查询了,鉴于查询基本都以键值方式进行,似乎直接使用BDB也是可以考虑的。当然你也可以在前面加入一个memcached做缓存层,进一步提高效率。

BTW:例子里body字段使用了MEDIUMBLOB类型,也就是说在入库前需要先把数据转换成二进制,PHP里似乎只能自己用pack函数去做,感觉不方便,而且效率或许是个大问题,所以使用TEXT类型保存序列化后的字符串似乎也是一个不错的选择,虽然体积大了些。

进入正题,假设body里有一个user_id属性,现在我们要索引它,那么可以新建一个类似下面的表:

CREATE TABLE index_user_id (
    user_id BINARY(16) NOT NULL,
    entity_id BINARY(16) NOT NULL UNIQUE,
    PRIMARY KEY (user_id, entity_id)
) ENGINE=InnoDB;


当需要通过user_id查询实体的时候,查询先从index_user_id表进行(此时进行的是CoveringIndex类型的查询,所以会相当快),得到符合条件的entity_id后,再从entities表里得到实体的具体数据,新建index_user_id表的时候并不会堵塞在entities表上进行的操作,所以这个过程是平滑的。

如果想添加字段就更简单了,别忘了entities表的body字段是一个属性大容器,它包含了实体大多数的属性。

FriendFeed的做法实际上是刻意弱化关系数据库中“关系”的影响,从某种意义上看,此时的关系数据库已经退化成了键值数据库,这么做当然有好处也有坏处,实际应用中是否适合就要具体情况具体分析了。就说这么多吧,更多的信息大家请参考原文。