详细内容
实体在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