热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

FriendFeed如何使用MySQL存储非关系性数据_howfriendfeedusesmysql

原文链接:http:bret.appspot.comentryhow-friendfeed-uses-mysql本文链接:http:virest.orghow-friendfeed

原文链接: http://bret.appspot.com/entry/how-friendfeed-uses-mysql

本文链接:  http://virest.org/how-friendfeed-uses-mysql-cn.html

著者: Bret Taylor

译者: khan.chan 

背景

在FriendFeed我们使用MySQL存储所有的数据.  用户的不断增长也让我们的数据库增长不少, 我们现在存储超过2.5亿个条目以及 其他一部分从评论和”likes”的朋友名单.

由于我们的数据库的增长, 我们试图处理可扩展的问题.  我们做些实质性的事,比如使用MySQL Slavers和memcached去增加吞吐量和分区数据库以提高写入的吞吐量.  然而,当我们不断增长,扩展我们现有的设置去适应更多的流量还不如去增加个新功能.

特别是,使架构更改或者增加索引到一个数据库将马上导致一个小时内超过 10-20万行完全锁在数据库. 删除旧索引只需要尽可能多的时间,不删除将影响性能, 因为数据库将在每一次INSERT继续读取和写入到这些未使用块. 有复杂的运作步骤让你绕过这些问题(像设置新索引在一个Slave,然后交换slave和master),但是这些步骤都极易出差错,新增特征时必须做架构/索引更改(they implicitly discouraged our adding features that would require schema/index changes). 由于我们的数据库分区,MySQL一些类似JOIN我们从未应用过, 所以我们决定寻找外部的RDBMS.

存在大量的项目,旨在解决这个额为难题的数据存储模式让其身轻如燕(比如CouchDB). 不过,他们似乎没有被广泛适用于大型网站, 在测试中,也无一项目可满足我们的需求.

经过一番考虑,我们坚决实施实现一个”schema-less”存储系统而不是用个新的存储系统.我们也好奇别的大型网站如何处理此类问题,我们想到了我们 所做的设计工作可能是有益的其他开发人员.

概况

我们的数据存储非结构化的属性(例如JSON对象或Python字典). 唯一要求存储的实体有id属性,一个16字节的UUID。实体的其他部分不透明,这样我们可以简单的通过增加新属性改变”schema”.

我们将索引保存在分开的MySQL表里来索引这些实体. 如需要索引实体的3个属性,那就需要3张表, 如果想停用一个索引,只需要代码里停止写入这个表,甚至于删除这个表.  如果希望增加一个新索引,可为这个索引建新表,运行一个进程异步迁移索引而不破坏我们的在线服务.

结果是,我们比以前有了 更多的表,但是增加和删除索引非常容易。我们大量地优化生成新索引的过程,因此它可以不中断站点快速创建索引。我们可以在白天而不是周末才存储新的属性和 索引,而且也不需要切换MySQL master和slave服务器.

详情

在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 主键确保新实体在老实体后被写入硬盘,实体机构的zlib压缩存储是python字典的 pickled序列化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;

我们的数据存储自动维护索引, 启动一个类似结构的数据存储实例(python):

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"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参数用于确定分区的索引存储在哪个分区.

你可以检索索引使用索引实例(user_id_index.get_all), 数据存储代码在index_user_id表和entities表间作”join”操作, 先在所有数据分区索引index_user_id表,获取实体ID的列表,再从entities表中获取这些实体ID.

添加一个新索引,例如,在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的工程师,但我们希望尽可能保持系统
简单,所以决定放宽限制:
. entities表中的属性包满足范式
. 索引可能不反映实际实体的值
因此,我们写入一个新的实体需要如下步骤:
1. 写入实体到entities表,使用InnoDB的ACID属性
2. 向所有分区上的索引表写入索引
当我们从索引表读的时候,我们知道 他们可能不精确。为了确保我们不返回非法的实体,我们使用索引表来确定要读取哪个实体,但是重新应用查询过滤条件:
1. 基于查询从所有索引表中读取entity_id
2. 从entities表中读取指定ID的实体
3. 在代码中根据实际的属性值过滤不符合查询条件的实体
为了确保索引能够被最终修复,"Cleaner"进程持续地运行,清除旧的和非法的索引.
它先 清除最近更新的实体,这样索引中的不一致可以非常快的被修复性能我们已经在这个新系统上优化了相当多的主要指标, 结果也非常让人高兴.
过去一个月FriendFeed PV数值:

特别是,我们的系统延迟非常稳定,哪怕是在繁忙时段.

比较一个星期前的:



推荐阅读
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • 使用正则表达式爬取36Kr网站首页新闻的操作步骤和代码示例
    本文介绍了使用正则表达式来爬取36Kr网站首页所有新闻的操作步骤和代码示例。通过访问网站、查找关键词、编写代码等步骤,可以获取到网站首页的新闻数据。代码示例使用Python编写,并使用正则表达式来提取所需的数据。详细的操作步骤和代码示例可以参考本文内容。 ... [详细]
  • Python SQLAlchemy库的使用方法详解
    本文详细介绍了Python中使用SQLAlchemy库的方法。首先对SQLAlchemy进行了简介,包括其定义、适用的数据库类型等。然后讨论了SQLAlchemy提供的两种主要使用模式,即SQL表达式语言和ORM。针对不同的需求,给出了选择哪种模式的建议。最后,介绍了连接数据库的方法,包括创建SQLAlchemy引擎和执行SQL语句的接口。 ... [详细]
  • 本文主要复习了数据库的一些知识点,包括环境变量设置、表之间的引用关系等。同时介绍了一些常用的数据库命令及其使用方法,如创建数据库、查看已存在的数据库、切换数据库、创建表等操作。通过本文的学习,可以加深对数据库的理解和应用能力。 ... [详细]
  • 合并列值-合并为一列问题需求:createtabletab(Aint,Bint,Cint)inserttabselect1,2,3unionallsel ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 解决VS写C#项目导入MySQL数据源报错“You have a usable connection already”问题的正确方法
    本文介绍了在VS写C#项目导入MySQL数据源时出现报错“You have a usable connection already”的问题,并给出了正确的解决方法。详细描述了问题的出现情况和报错信息,并提供了解决该问题的步骤和注意事项。 ... [详细]
  • 本文详细介绍了MySQL表分区的创建、增加和删除方法,包括查看分区数据量和全库数据量的方法。欢迎大家阅读并给予点评。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • 本文提供了关于数据库设计的建议和注意事项,包括字段类型选择、命名规则、日期的加入、索引的使用、主键的选择、NULL处理、网络带宽消耗的减少、事务粒度的控制等方面的建议。同时还介绍了使用Window Functions进行数据处理的方法。通过遵循这些建议,可以提高数据库的性能和可维护性。 ... [详细]
author-avatar
我要减肥2502896373
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有