FriendFeed如何使用MySQL存储无Schema数据(二)

详细内容

实体在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_id字段,是因为InnoDB会把主键大小作为物理顺序存储每行数据。AUTO_INCREMENT主键可以保证新的实体在写入磁盘是按顺序放在旧的实体后面,这样做也对读写的区域性有所帮助(新实体的读取频度要比旧实体高很多,因为FriendFeed的页面是按时间逆序来排序的)。实体的主题部分的存储格式,是进行了pickle(请参阅参考资料4)序列化的Python dictionary,并在此之上做了zlib压缩。

索引被分开存储在独立的表中。要创建新的索引,我们要先创建数据表,在所有数据库分片上保存我们希望索引的属性。比如,一个典型的FriendFeed实体看起来会像这样:

{

“id”:”71f0c4d2291844cca2df6f486e96e37c”,

“user_id”:”f48b0440ca0c4f66991c4d5f6a078eaf”,

“feed_id”:”f48b0440ca0c4f66991c4d5f6a078eaf”,

“title”:”We just launched a new backend system for FriendFeed!”,

“link”:”http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c”,

“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;

我们的datastore会自动为你维护索引,因而,要启动存储上述结构并带有给定索引的实体的一个datastore实例,(在Pyton中)你可以这样写:

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”:”71f0c4d2291844cca2df6f486e96e37c”,

“user_id”:”f48b0440ca0c4f66991c4d5f6a078eaf”,

“feed_id”:”f48b0440ca0c4f66991c4d5f6a078eaf”,

“title”:”We just launched a new backend system for FriendFeed!”,

“link”:”http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c”,

“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参数的作用就是用来决定要把索引存储在哪一个分片上(在本例中是entity["user_id"] % num_shards)。

借助这个索引的实例,你可以查询索引(参见上例中的user_id_index.get_all)。通过在所有数据库分片查询index_user_id表,获取实体的ID列表,接着从entities表中取出相应ID值的实体,datastore的代码就可以在Python中完成index_user_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;

再把datastore改成下面这样,新索引就被包含进来了:

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])

接着,运行下面的命令,我们就可以用异步的方式进行索引的传播(甚至在网站处理实时请求的情况下都没问题):

vv ./rundatastorecleaner.py –index=index_link

留下回复