微博平台分布式存储系列--Sharding With Mysql(上)

Posted 微博平台架构

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微博平台分布式存储系列--Sharding With Mysql(上)相关的知识,希望对你有一定的参考价值。

在当今互联网,存储产品林林总总数以百计,但mysql仍然最受欢迎。在Facebook、微博等互联网在线核心应用数据,仍然使用Mysql存储。面对海量数据时,mysql如何应对?如何解决容量、访问量、可用性等问题?容平台君慢慢与您分享。


Partition把IO离散到不同磁盘 存储产品大多是IO密集型,传统的方式将同一张表的数据存储同一个文件中,在读写量上升时,会因为IO、锁等资源争用碰到瓶颈。一种可行的方式就是分区(Partition),按照某种策略将同一张表的数据离散到不同文件中,通常的做法有两种


垂直分区

以微博的核心业务timeline举例。最经典的应用场景包括:1) 我的微博列表; 2) 按微博ID查找微博。在每次新同学入职的时候拿这个作为毕业设计题目时,大多同学会创建一张表(uid与mid索引等信息未列出)

Field

Type

uid

bigint(x) unsigned

mid

bigint(x) unsigned

status

tinyint unsigned

content

varbinary(x) unsigned

上述设计在真实流量模拟下很难达到线上的读写效果,究其原因:

  • content列长度长达数k,其它一共几十字节,在不同的访问模型下mysql缓存命中率会相互影响;

  • status、mid与content的读取、更新频率不一,更新时会导致锁争用,影响整体性能;

  • 在分布式场景下,无法同时按照uid&mid进行sharding(下文会讨论sharding)

基于这样的考虑,微博线上的表设计将其垂直拆分成两张表,一张为Feed索引表(下面简称Feed表)包含uid与mid的映射关系(uid,mid,status...);另一张为内容表,包含id与内容的映射关系(mid,content...)。


水平分区

在mysql中,通常可以将索引文件与数据文件存储到不同的磁盘上以提升整体的吞吐。但面对以十计的磁盘数量时,仍难以充分利用。目前主流的数据库支持将同一张表的数据,按照某种策略将不同的数据进行分区,不同的分区存储在不同的文件中,以提升磁盘IO的利用率。Mysql的分区策略主要有RANGE、LIST、HASH、KEY。

水平分区能够有效利用磁盘的IO,但为mysql带来了额外的管理负担,在现实场景中,可通过垂直分区+不同业务混跑即可充分利用磁盘IO,因此微博并未针对单机单表进行分区。但是其思路可以推广到分布式场景中,以解决单机的IO与容量问题。


Sharding把IO与容量离散到不同机器 微博单业务数据规模达到千亿,DB访问量达到十万QPS,远远超出单机IO与容量。最直接的解决思路就是分而治之:一台机器存储一部分数据。这个过程就是Sharding。与单机水平分区类似,Sharding包括sharding key、策略、路由、resharding等,下面结合微博Feed场景进行说明。


应用场景

微博Feed的应用场景可抽象成一句SQL

select mid from feed where uid=$uid limit offset, count;

其中: uid: 用户id;mid:微博id


选择Sharding Key

sharding key一般可以是一列或者是多列的组合,为了描述方便,这里只讨论一列的情况。究竟选择哪一列一般与具体的业务场景相关,但一般而言,采用主键或者复合主键的第一列。

在Feed表中,使用uid做sharding key。即同一个用户的所有数据(发表的微博),在同一个shard中。


Shard的数量与规模

一般来说,shard的数量没有理论上的限制。但在实践过程中,一般要考虑:

  • 单机磁盘容量 尽量确保磁盘的容量&IO能够服务于至少一个shard。尽量指的是,当一台机器无法承担一个shard的服务时,通过split将一个shard离散到不同的机器。

  • cross-shard查询 比如要查询200个用户的微博数据,如果所有的数据都存储在一个shard里面,批量检索只需要一次网络IO,在一台机器上的一个或者少数几个文件中就能检索到满足条件的数据;相反,如果shard数量过多,则极端情况下需要检索200次。

所以说,shard的数量与规模的选择是一个trade-off,需要结合具体的数据规模与业务场景。业界有两种典型的场景:

大量小规模shard


在Facebook的用户信息场景中,每个shard会包含有成千上万用户的相关数据,由此计算,shard的数量以万计。

少量大规模shard


在微博的Feed场景中,主要的应用场景就是聚合多个好友的微博数据,因此Feed采取了较少数量的shard: 一般为8的倍数(32个),每个shard的数据量数十亿的规模(超出单机容量?后面会讨论)。在实际应用场景中,由于业务端缓存的存在,穿透到DB查询微博的用户数量远小于200,一般在数个。在这种场景下,shard的数量可适当增加。


Sharding的策略

确定完sharding的key与规模后,需要建立起key到shard的映射关系,以进行数据的存取。sharding策略的基本原则就是尽可能的将数据容量、请求量、对磁盘IO的消耗均衡的分散到不同的shard上。目前常用的主要有三种策略:

Range


按照key的范围确定shard的边界。如从[0,1000)的范围放在shard-0,[1000,2000)放在shard-1上,以此类推。这样的key一般带有时间属性,即随着时间的推移key会增长。比如:自增的id等等。

Hash


有一些key没有时间序,又或者按照时间序或者字典序进行Range划分时会带来问题。微博的uid是按创建时间有序的,即若t1>t0则uid1>uid0。但问题是:

  • 如果按照Range来就会出现某些shard的请求量过高。因为某些时间段(如微博创建早期)创建的uid活跃度更高;

  • 由于uid生成规则难以确保不同区间包含的用户数量基本相同。

在这种情况下,Hash能够确保数据容量、请求量以及对磁盘IO的消耗更加均衡。事实上,Feed的采取的就是Hash策略。

计算出Hash值之后,通过hash % num of shards的方式计算对应的key所属的shard.

List


可以理解为是Range的一个变种,只是每一个Range区间key的取值只有唯一的一个。

基于上面的分析,结合微博uid的特性,Feed使用Hash策略。在使用Hash策略初期,发现每一个shard服务的uid数量仍然不均衡,分析后发现,相近的uid更容易被hash到相同的shard中,因此在hash之前对uid进行了crc32处理。即


shard-index = hash(crc32(uid)) % num of shards

通过这种方式,基本确保不同shard之间的负载均衡。


Sharding、mysqld及database的关系

一个shard对应一个database。在容量、IO等资源足够的情况下,一个mysqld包含一个或者多个shard。具体的关系如下图:


Shard Proxy

负责client到具体数据库实例的路由。其维护了sharding相关的所有信息:sharding key、策略、数量,mysqld与shard的隶属关系及与mysqld的连接管理。

Proxy通过域名与mysqld进行连接,由自定义域名服务负责域名背后机器的变更、下线、故障以及请求的负载均衡等问题等(具体实现策略在后面的章节会有专门的描述)。


So Easysharding就这么简单 微博的Feed就是通过这种方式将千亿级别的数据存储到数百台服务器上,支撑了十万级别的QPS。简单吧,这就对了。KISS嘛。


Are You Kidding Me?玩笑开大了 开玩笑吧,sharding真这么简单?如果数据量是静态的、访问量是均衡的、机器是不出问题的,的确就这么简单。凡事都有个但是:
  • 8个mysqld不足以容量全量数据时怎么办?

  • 总量数据1000亿 / 32 shard = 每个shard30亿+,这些数据存储到1张表中,性能问题怎么办?

  • 某个shard因为某种原因,访问量显著高于别的shard导致不均衡时怎么办?

  • 某个shard不可用时怎么办?

  • 如何解决shard中数据的冷热访问不均衡问题以节省成本?

  • shard proxy就是个黑盒,他有什么魔法?

将在接下来的中、下篇逐一回答这些问题。



最后,欢迎大家关注 @微博平台架构 的微信、微博。回复“1”可了解更多团队信息(有清晰剧照),欢迎长按图片扫码关注。


以上是关于微博平台分布式存储系列--Sharding With Mysql(上)的主要内容,如果未能解决你的问题,请参考以下文章

巨杉TechDay回顾 | 分布式数据库@民生Sharding Sphere@京东ClickHouse@微博

Sharding-Sphere系列-主从配置和分库分表

基于 MongoDB 的分布式数据库架构 Sharding

微博广告Hubble系统:秒级大规模分布式智能监控平台架构实践

Akka Cluster Sharding

分布式事务在Sharding-Sphere中的实现(有彩蛋)