使用FriendFeed来提升MySQL性能的方法金沙8331网址

背景

作者们运用MySQL存储了FriendFeed的兼具数据。数据库随着客户基数的滋长而狠抓了数不胜数。现在早就积攒了赶上2.5亿条记下与一群包涵了从评价和“合意”到相守列表的其它数据。

乘胜数据的拉长,大家也曾迭代地清除了乘胜那样高效的增进而带来的扩张性难点。大家的尝尝很有代表性,举例利用只读mysql从节点和memcache来扩张读取吞吐量,对数据库进行分片来拉长写入吞吐量。但是,随着专门的学业的增高,增多新功效比增添既有功用以迎合越多的流量变得进一层辛勤。

专程的,对 schema 做订正或为超过 1000-2003万行记录的数据库加多索引会将数据库锁住多少个钟头。删除旧索引也要私吞这么多时光,但不删除它们会潜移默化属性;因为数据库要不断地在各样INSERT上读写那一个没用的区块,并将第生机勃勃的区块挤出了内部存款和储蓄器。为幸免这几个主题材料必要动用部分繁杂的诀窍,但这一个艺术会抓住错误而且实行起来相比较劳苦,它们阻碍了索要改换schema/索引才干完成的新功效。由于数据库的深重分散,MySQL
的关联特性(如join)对大家没用,所以我们决定退出 EnclaveDBMS。

纵然本来就有为数不菲用来缓和灵活 schema 数据存款和储蓄和周转时构建索引的主题素材(比方CouchDB卡塔尔的种类。但在大站点中却尚无丰裕遍布地用到来讲性格很顽强在费力辛勤或巨大压力面前不屈人们采纳。在大家看见和平运动转的测量检验中,那一个项目照旧不平稳,要么贫乏丰富的测量试验(参见这么些有一些过时的有关
CouchDB 的篇章卡塔尔。MySQL
不错,它不会毁掉数据;复制也没难点,大家曾经领悟了它的局限。大家爱怜将
MySQL 用于存款和储蓄,仅仅是非关系型的储存。

几次经过思考,大家决定在 MySQL
上选择大器晚成种无形式的仓库储存系统,实际不是运用一个一心没接触过的存款和储蓄系统。本文试图描述那几个系统的高档细节。我们很诧异其余大型网址是什么样处理那么些主题材料的,其余也希望大家成功的少数设计会对其余开荒者有所补助。

综述

我们在数据库中寄存的是无形式的属性集(举例JSON对象或python词典卡塔尔。存款和储蓄的笔录只需三个名叫id的16字节的UUID属性。对数据库来说实体的此外一些是不可以看到的。大家得以总结地存入新属性来改动schema(可以大约明了为数据表中只有三个字段:id,data;当中data存款和储蓄的是实体的属性集卡塔尔。

大家经过保留在不一致表中的目录来寻觅数据。借使想搜寻各个实体中的多少个天性,大家就需求三个数据表-各样表用于检索某风流洒脱特定属性。假诺不想再用某一索引了,大家要在代码中截至该索引对应表的写操作,并可选地删除这几个表。假若想加多个新索引,只供给为该索引新建个MySQL表,并运行三个历程异步地为该表增添索引数据(不影响运转中的服务)。

末段,尽管我们的数量表增加了,但丰裕和删除索引却变得轻巧了。大家大力改正了增加索引数据的历程(大家誉为“清洁工”卡塔尔(قطر‎使其在连忙增加索引的还要不会潜濡默化站点。大家能够在一天内实现新属性的保留和目录,并且大家无需对调主从MySQL数据库,也没有供给任何此外可怕的操作。

细节

MySQL 使用表保存咱们的实体,一个表就像是这么 :

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_使用FriendFeed来提升MySQL性能的方法金沙8331网址。id 个字段是因为 InnoDB
按物理主键顺序存款和储蓄数据,自增加主键确认保证新实例在磁盘上按顺序写到赤诚体之后,那样有利于分区读写。实体自身经
python 词典体系化后使用 zlib 压缩存款和储蓄。

目录单独存在一张表里,假使要创设索引,我们创设一张新表存款和储蓄大家想要索引的数据分片的具备属性。举例,二个FriendFeed 实体通过看上去是这么的:

{ "id": "71f0c4d2291844cca2df6f486e96e37c", "user_id": "f48b0440ca0c4f66991c4d5f6a078eaf", "feed_id": "f48b0440ca0c4f66991c4d5f6a078eaf", "title": "We just launched a new backend system for FriendFeed!", "link": "", "published": 1235697046, "updated": 1235697046,}

大家索引实体的性能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 = friendfeed.datastore.Index( table="index_user_id", properties=["user_id"], shard_on="user_id")datastore = friendfeed.datastore.DataStore( mysql_shards=["127.0.0.1:3306", "127.0.0.1:3307"], indexes=[user_id_index]) new_entity = { "id": binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c"), "user_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"), "feed_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"), "title": u"We just launched a new backend system for FriendFeed!", "link": u"", "published": 1235697046, "updated": 1235697046,}datastore.put(new_entity)entity = datastore.get(binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c"))entity = user_id_index.get_all(datastore, user_id=binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"))

地点的 Index 类在具备实体中找找 user_id,自动爱慕 index_user_id
表的目录。我们的数据库是切分的,参数 shard_on
是用来鲜明索引是储存在哪个分片上查询一个目录,使用 python
写的数据存款和储蓄代码将表 index_user_id 和表 entities
合并。首先在享有数据库分片中查询表 index_user_id 获取实体 ID
列,然后在 entities 建议数据。

新建一个目录,比方,在质量 link 上,我们得以成立三个新表:

CREATE TABLE index_link ( link VARCHAR(735) NOT NULL, entity_id BINARY(16) NOT NULL UNIQUE, PRIMARY KEY (link, entity_id)) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我们得以校订数据存款和储蓄的初始化代码以带有大家的新索引:

user_id_index = friendfeed.datastore.Index( table="index_user_id", properties=["user_id"], shard_on="user_id")link_index = friendfeed.datastore.Index( table="index_link", properties=["link"], shard_on="link")datastore = friendfeed.datastore.DataStore( mysql_shards=["127.0.0.1:3306", "127.0.0.1:3307"], indexes=[user_id_index, link_index])

自己得以异步营造索引:

./rundatastorecleaner.py --index=index_link

黄金年代致性与原子性

由于使用分区的数据库,实体的目录也许存款和储蓄在与实体分歧的分区中,那引起了黄金年代致性难题。假设经过在写入全部索引表前崩溃了会如何?

洋洋有野心的 FriendFeed
程序猿帮衬于营造二个事务性构和,但大家愿意尽量地保持系统的洗练。我们决定放宽限定:

封存在主实体表中的属性集是规范完整的 索引不会对真实实体值发生震慑

故而,往数据库中写入实体时大家采纳如下步骤:

运用 InnoDB 的 ACID 属性将实体写入 entities 表。
将引得写入全数分区中的索引表。

咱俩要记住从索引表中抽取的数量恐怕是不正确的。为保障选择地点的节制能回去正确的实体,大家用索引表来调控要读取哪些实体,但不用相信索引的完整性,要使用查询条件对那几个实体进行再过滤:

1.基于查询条件从索引表中赢得 entity_id

2.根据 entity_id 从 entities 表中读取实体

3.基于实体的真实性属性过滤掉不切合查询条件的实体

为保障索引的长久性和生机勃勃致性,上文提到的“清洁工”进程要持续运维,写入遗失的目录,清理失效的旧索引。它预先清理方今立异的实业,所以其实维护索引的生机勃勃致性超级快(几分钟State of Qatar.性能

笔者们对新种类的主索引举行了优化,对结果也很恋慕。以下是本月 FriendFeed
页面包车型地铁加载延时总计图。

特意地,系统的延时现行反革命也很牢固。如下是过去24小时FriendFeed页面加载延时图。

与下18日的某天相比较:

系统到近年来结束使用起来很有利。咱们在构造之后也改成了两遍索引,何况大家也初始将这种方式采纳于
MySQL 中那一个超大的表,那样我们在其后能够轻易地改成它们的构造。

发表评论

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