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

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

著者: Bret Taylor

译者: khan.chan  <khan.chan {at} virest.org>

背景

在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数值:

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

比较一个星期前的:

FriendFeed如何使用MySQL存储非关系性数据_how-friendfeed-uses-mysql相关推荐

  1. mysql支持非关系_说下oracle、mysql、非关系型数据库中的索引结构?

    谢邀~~树懒君悉心整理了一篇索引结构方面的内容,跟各位知友分享分享~ Oracle 索引的数据结构:B-TreeOracle 数据库使用 B-trees 存储索引,来加速数据访问.若没有索引,你必须顺 ...

  2. 如何使用 SQL Server FILESTREAM 存储非结构化数据?这篇文章告诉你!

    作者 | ALEN İBRIÇ 译者 | 火火酱,责编 | Carol 封图 | CSDN 付费下载于视觉中国 在本文中,我将解释如何使用SQL Server FILESTREAM来存储非结构化数据. ...

  3. 如何使用 SQL Server FILESTREAM 存储非结构化数据?

    作者 | ALEN İBRIÇ 译者 | 火火酱,责编 | Carol 封图 | CSDN 付费下载于视觉中国 在本文中,我将解释如何使用SQL Server FILESTREAM来存储非结构化数据. ...

  4. mysql 存储引擎 面试_搞定PHP面试 - MySQL基础知识点整理 - 存储引擎

    MySQL基础知识点整理 - 存储引擎 0. 查看 MySQL 支持的存储引擎 可以在 mysql 客户端中,使用 show engines; 命令可以查看MySQL支持的引擎: mysql> ...

  5. mysql存储引擎使用教程_mysql教程:如何写MySQL存储引擎

    在MySQL 5.1中开发一个存储引擎已经是比较方便了.所谓存储引擎实际上是按照MySQL的约定,提供某些接口的实现而已,如MySQL插入一条记录时将调用write_row方法,通过索引检索时将调用i ...

  6. ubuntu设置mysql可以非本地访问_ubuntu server下设置mysql的远程访问权限

    安装mysql 安装mysql的方式较多主要有使用源安装,使用本地压缩包进行安装的方式. 具体参照 [ubuntu安装mysql5.5] 检查mysql的远程访问权限 en@iZ944qtakg9Z: ...

  7. mysql ssd inodb___细看InnoDB数据落盘 图解 MYSQL

    1.  概述 前面很多大侠都分享过MySQL的InnoDB存储引擎将数据刷新的各种情况.我们这篇文章从InnoDB往下,看看数据从InnoDB的内存到真正写到存储设备的介质上到底有哪些缓冲在起作用. ...

  8. mysql 执行计划 改变_数据量增加导致mysql执行计划改变解决_MySQL

    bitsCN.com 数据量增加导致mysql执行计划改变解决 收到运维同学电话,mysql服务器连接数满了,登录服务器查看,确实满了,好吧,首先增加连接数到2500,暂时提供对外服务.连接继续升高, ...

  9. mysql存储数组类型的数据_mysql中怎么存储数组

    展开全部 SQL没有数组这种类型,数组是一种数据结构的概念,跟关系型mysql数据存储32313133353236313431303231363533e78988e69d833133343362313 ...

最新文章

  1. Level3公司在哥伦比亚开通运营第三个数据中心
  2. 机器学习的优化目标、期望最大化(Expectation-Maximum, EM)算法、期望最大化(EM)和梯度下降对比
  3. mmz-asio4delphi死链接的解决办法
  4. 物联网产业迎资本热潮 孤岛困局亟待打破
  5. 使用Javascript递归遍历本地文件夹
  6. java linux 服务_java项目部署Linux服务器几种启动方式总结经验
  7. 在python中、如果异常并未被处理或捕捉_7、Python-异常
  8. Linux 基础——ls 命令
  9. tensorRT程序设计框架_4
  10. Dictionary 索引超出数组界限
  11. 服务器安装动易组件,动易SiteWeaver6.8安装方法
  12. 【CG物理模拟系列】流体模拟--粒子法之MPS法(理论)
  13. 深澜系统服务器架构,S7510E-X结合深澜服务器做Portal无感知认证终端不定时掉线经验案例...
  14. 按“window+E”键出现【找不到应用程序】或【explore.exe找不到】的解决方法
  15. 2018互联网月饼哪家壕,阿里腾讯网易走着瞧
  16. GitLab-Runner 从安装到配置到入门
  17. 聊聊我在阿里所经历的新零售业务商品中心微服务化的过程
  18. 大数据实验一 关联规则实验题目:蔬菜价格相关性分析
  19. vue移动端下拉刷新组件、上拉加载组件
  20. 网线连接olt配置计算机IP,OLT配置上行以太网端口属性

热门文章

  1. 华为P50Pro手机无法连接打印机?
  2. USB声卡之时钟模式分析
  3. 计算机办公软件应用三套题目,Office办公软件高级应用第三套试卷100分.doc
  4. 张迈机器人_第一季度“新时代好少年”事迹简介
  5. loadrunner运行测试场景出现Two Way Communication Error
  6. 阿里云云计算ACP学习(三)---对象存储OSS
  7. 开发属于自己的,iPhone设备管理工具,iTunesMobileDevice伪帮助文档
  8. 快乐教鞭2.0新增功能介绍
  9. tensor.max()
  10. 数学建模用matlab还是python_参加数学建模用 MATLAB,还是 Python?