规避MySQL中DDL的堵塞 — 解读FriendFeed对MySQL的使用

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

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

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

再一个方法是利用主从服务器绕开添加索引之类的DDL堵塞问题。常见的主从结构一般是一主多从,写操作在主服务上完成,读操作由多台从服务器共同承担。当我们想添加索引的时候,其实就是给从服务器添加索引,此时可以先让一台从服务器下线(slave stop),并按常规方式在这台从服务器上添加索引,期间网站的查询服务由其它从服务器代劳,索引添加完毕后,重新把这台从服务器上线(slave start),周而复始,直到所有的从服务器都添加了索引。整个过程对网站服务的使用来说基本是透明的,可惜这个方法有弊端:如果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字段,FriendFeed把实体属性都转换成二进制压缩后保存到这个字段里,实际使用的时候,当你通过id定位到实体后,需要的数据都可以从这个字段得到,就不需要再像传统做法那样和其它表进行JOIN之类的连接查询了,当然你也可以在entities表的前面加入一个memcached做缓存层,进一步提高效率。

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

进入正题,假设我们要索引实体的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表进行(此时进行的是Covering Index类型的查询,所以会相当快),得到符合条件的entity_id后,再从entities表里得到实体的具体数据,新建index_user_id表的时候并不会堵塞在entities表上进行的操作,所以这个过程是平滑的。

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

就说这么多吧,更多的信息大家请参考原文

觉得文章有用?立即: 和朋友一起 共学习 共进步!

猜您喜欢

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>