分库分表技术演进&最佳实践

每一个优秀的程序员和架构师都应该掌握分库分表,这是个人观点。程序员

 

移动互联网时代,海量的用户天天产生海量的数量,好比:数据库

  • 用户表缓存

  • 订单表微信

  • 交易流水表网络

 

以支付宝用户为例,8亿;微信用户更是10亿。订单表更夸张,好比美团外卖,天天都是几千万的订单。淘宝的历史订单总量应该百亿,甚至千亿级别,这些海量数据远不是一张表能Hold住的。事实上MySQL单表能够存储10亿级数据,只是这时候性能比较差,业界公认MySQL单表容量在1KW如下是最佳状态,由于这时它的BTREE索引树高在3~5之间。架构

 

既然一张表没法搞定,那么就想办法将数据放到多个地方,目前比较广泛的方案有3个:并发

  1. 分区;运维

  2. 分库分表;elasticsearch

  3. NoSQL/NewSQL;高并发

 

说明:只分库,或者只分表,或者分库分表融合方案都统一认为是分库分表方案,由于分库,或者分表只是一种特殊的分库分表而已。NoSQL比较具备表明性的是MongoDB,es。NewSQL比较具备表明性的是TiDB。

 

Why Not NoSQL/NewSQL?

 

首先,为何不选择第三种方案NoSQL/NewSQL,我认为主要是RDBMS有如下几个优势:
   - RDBMS生态完善;
   - RDBMS绝对稳定;
   - RDBMS的事务特性;

 

NoSQL/NewSQL做为新生儿,在咱们把可靠性当作首要考察对象时,它是没法与RDBMS相提并论的。RDBMS发展几十年,只要有软件的地方,它都是核心存储的首选。

 

目前绝大部分公司的核心数据都是:以RDBMS存储为主,NoSQL/NewSQL存储为辅!互联网公司又以MySQL为主,国企&银行等不差钱的企业以Oracle/DB2为主!NoSQL/NewSQL宣传的不管多牛逼,就如今各大公司对它的定位,都是RDBMS的补充,而不是取而代之!

 

Why Not 分区?

 

咱们再看分区表方案。了解这个方案以前,先了解它的原理:

分区表是由多个相关的底层表实现,这些底层表也是由句柄对象表示,因此咱们也能够直接访问各个分区,存储引擎管理分区的各个底层表和管理普通表同样(全部的底层表都必须使用相同的存储引擎),分区表的索引只是在各个底层表上各自加上一个相同的索引,从存储引擎的角度来看,底层表和一个普通表没有任何不一样,存储引擎也无须知道这是一个普通表仍是一个分区表的一部分。

 

事实上,这个方案也不错,它对用户屏蔽了sharding的细节,即便查询条件没有sharding column,它也能正常工做(只是这时候性能通常)。不过它的缺点很明显:不少的资源都受到单机的限制,例如链接数,网络吞吐等!虽然每一个分区能够独立存储,可是分区表的总入口仍是一个MySQL示例。从而致使它的并发能力很是通常,远远达不到互联网高并发的要求!

 

至于网上提到的一些其余缺点好比:没法使用外键,不支持全文索引。我认为这都不算缺点,21世纪的项目若是仍是使用外键和数据库的全文索引,我都懒得吐槽了!

 

因此,若是使用分区表,你的业务应该具有以下两个特色:

  • 数据不是海量(分区数有限,存储能力就有限);

  • 并发能力要求不高;

 

Why 分库分表?

 

最后要介绍的就是目前互联网行业处理海量数据的通用方法:分库分表。

 

虽然你们都是采用分库分表方案来处理海量核心数据,可是尚未一个一统江湖的中间件,笔者这里列举一些有必定知名度的分库分表中间件:

  • 阿里的TDDL,DRDS和cobar,

  • 开源社区的sharding-jdbc(3.x已经改名为sharding-sphere);

  • 民间组织的MyCAT;

  • 360的Atlas;

  • 美团的zebra;

     

 

 

备注:sharding-jdbc的做者张亮大神原来在当当,如今在京东金融。可是sharding-jdbc的版权属于开源社区,不是公司的,也不是张亮我的的!

 

其余好比网易,58,京东等公司都有自研的中间件。总之各自为战,也能够说是百花齐放。

 

可是这么多的分库分表中间件所有能够归结为两大类型:

  • CLIENT模式;

  • PROXY模式;

 

CLIENT模式表明有阿里的TDDL,开源社区的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已经支持了proxy模式)。架构以下:

cb19b990885bf0111814cefbf6743b2bec158eaa

PROXY模式表明有阿里的cobar,民间组织的MyCAT。架构以下:

41c35eed0030db88ff112b7e65d6dd25f3e0edb8

可是,不管是CLIENT模式,仍是PROXY模式。几个核心的步骤是同样的:SQL解析,重写,路由,执行,结果归并。

 

笔者比较倾向于CLIENT模式,架构简单,性能损耗较小,运维成本低。

 

接下来,以几个常见的大表为案例,说明分库分表如何落地!

 

实战案例

 

分库分表第一步也是最重要的一步,即sharding column的选取,sharding column选择的好坏将直接决定整个分库分表方案最终是否成功。而sharding column的选取跟业务强相关,笔者认为选择sharding column的方法最主要分析你的API流量,优先考虑流量大的API,将流量比较大的API对应的SQL提取出来,将这些SQL共同的条件做为sharding column。例如通常的OLTP系统都是对用户提供服务,这些API对应的SQL都有条件用户ID,那么,用户ID就是很是好的sharding column。

 

这里列举分库分表的几种主要处理思路:

  • 只选取一个sharding column进行分库分表 ;

  • 多个sharding column多个分库分表;

  • sharding column分库分表 + es;

 

再以几张实际表为例,说明如何分库分表。

 

  • 订单表

 

订单表几个核心字段通常以下:

e3e0fb683c6f944071a2dbd744f7fe4e5318e18d

以阿里订单系统为例(参考《企业IT架构转型之道:阿里巴巴中台战略思想与架构实现》),它选择了三个column做为三个独立的sharding column,即:order_id,user_id,merchant_code。user_id和merchant_code就是买家ID和卖家ID,由于阿里的订单系统中买家和卖家的查询流量都比较大,而且查询对实时性要求都很高。而根据order_id进行分库分表,应该是根据order_id的查询也比较多。

 

这里还有一点须要说起,多个sharding-column的分库分表是冗余全量仍是只冗余关系索引表,须要咱们本身权衡。

 

冗余全量的状况以下--每一个sharding列对应的表的数据都是全量的,这样作的优势是不须要二次查询,性能更好,缺点是比较浪费存储空间(浅绿色字段就是sharding-column):

afa235e621afa54b1804fba920cb172417783946

冗余关系索引表的状况以下--只有一个sharding column的分库分表的数据是全量的,其余分库分表只是与这个sharding column的关系表,这样作的优势是节省空间,缺点是除了第一个sharding column的查询,其余sharding column的查询都须要二次查询,这三张表的关系以下图所示(浅绿色字段就是sharding column):

feb94b808252974cc6db43d44bd7f078ee29351e

冗余全量表PK.冗余关系表

  • 速度对比:冗余全量表速度更快,冗余关系表须要二次查询,即便有引入缓存,仍是多一次网络开销;

  • 存储成本:冗余全量表须要几倍于冗余关系表的存储成本;

  • 维护代价:冗余全量表维护代价更大,涉及到数据变动时,多张表都要进行修改。

总结:选择冗余全量表仍是索引关系表,这是一种架构上的trade off,二者的优缺点明显,阿里的订单表是冗余全量表。

 

  • 用户表

 

用户表几个核心字段通常以下:

620e4f9d5a2caca329e9d5f1716175cb5e1f8146

通常用户登陆场景既能够经过mobile_no,又能够经过email,还能够经过username进行登陆。可是一些用户相关的API,又都包含user_id,那么可能须要根据这4个column都进行分库分表,即4个列都是sharding-column。

 

  • 帐户表

 

帐户表几个核心字段通常以下:

09c7ec83874627bfd18482aeadae1f99d89740a6

与帐户表相关的API,通常条件都有account_no,因此以account_no做为sharding-column便可。

 

  • 复杂查询

 

上面提到的都是条件中有sharding column的SQL执行。可是,总有一些查询条件是不包含sharding column的,同时,咱们也不可能为了这些请求量并不高的查询,无限制的冗余分库分表。那么这些条件中没有sharding column的SQL怎么处理?以sharding-jdbc为例,有多少个分库分表,就要并发路由到多少个分库分表中执行,而后对结果进行合并。具体如何合并,能够看笔者sharding-jdbc系列文章,有分析源码讲解合并原理。

 

这种条件查询相对于有sharding column的条件查询性能很明显会降低不少。若是有几十个,甚至上百个分库分表,只要某个表的执行因为某些因素变慢,就会致使整个SQL的执行响应变慢,这很是符合木桶理论。

 

更有甚者,那些运营系统中的模糊条件查询,或者上十个条件筛选。这种状况下,即便单表都很差建立索引,更不要说分库分表的状况下。那么怎么办呢?这个时候大名鼎鼎的elasticsearch,即es就派上用场了。将分库分表全部数据全量冗余到es中,将那些复杂的查询交给es处理。

 

淘宝个人全部订单页面以下,筛选条件有多个,且商品标题能够模糊匹配,这即便是单表都解决不了的问题(索引知足不了这种场景),更不要说分库分表了:

d3c74a1aca60e57d60a25105d6d0bbad10852107

因此,以订单表为例,整个架构以下:

070e3a08af3b46efc318b222a86853a7bf1c03d7

具体状况具体分析:多sharding column不到万不得已的状况下最好不要使用,成本较大,上面提到的用户表笔者就不太建议使用。由于用户表有一个很大的特色就是它的上限是确定的,即便全球70亿人全是你的用户,这点数据量也不大,因此笔者更建议采用单sharding column + es的模式简化架构。

 

  • es+HBase简要

 

这里须要提早说明的是,solr+HBase结合的方案在社区中出现的频率可能更高,本篇文章为了保持一致性,全部全文索引方案选型都是es。至于es+HBase和solr+HBase孰优孰劣,或者说es和solr孰优孰劣,不是本文须要讨论的范畴,事实上也没有太多讨论的意义。es和solr本就是两个很是优秀且旗鼓至关的中间件。

 

最近几年es更火爆:

758ede44802c9da0fee72f7f80d988976562b602

若是抛开选型过程当中全部历史包袱,单论es+HBase和solr+HBase的优劣,很明显后者是更好的选择。solr+HBase高度集成,引入索引服务后咱们最关心,也是最重要的索引一致性问题,solr+HBase已经有了很是成熟的解决方案一一Lily HBase Indexer。

 

  • 延伸阅读

 

阿里云上的云数据库HBase版也是借助solr实现全文索引。

31786dad1ca754e360b7ee4a157bb357160e5c08

  • es+HBase原理

 

刚刚讨论到上面的以MySQL为核心,分库分表+es的方案,随着数据量愈来愈来,虽然分库分表能够继续成倍扩容,可是这时候压力又落到了es这里,这个架构也会慢慢暴露出问题!

 

通常订单表,积分明细表等须要分库分表的核心表都会有好几十列,甚至上百列(假设有50列),可是整个表真正须要参与条件索引的可能就不到10个条件(假设有10列)。这时候把50个列全部字段的数据全量索引到es中,对es集群有很大的压力,后面的es分片故障恢复也会须要很长的时间。

 

这个时候咱们能够考虑减小es的压力,让es集群有限的资源尽量保存条件检索时最须要的最有价值的数据,即只把可能参与条件检索的字段索引到es中,这样整个es集群压力减小到原来的1/5(核心表50个字段,只有10个字段参与条件),而50个字段的全量数据保存到HBase中,这就是经典的es+HBase组合方案,即索引与数据存储隔离的方案。

 

Hadoop体系下的HBase存储能力咱们都知道是海量的,并且根据它的rowkey查询性能那叫一个快如闪电。而es的多条件检索能力很是强大。这个方案把es和HBase的优势发挥的淋漓尽致,同时又规避了它们的缺点,能够说是一个扬长避免的最佳实践。

 

它们之间的交互大概是这样的:先根据用户输入的条件去es查询获取符合过滤条件的rowkey值,而后用rowkey值去HBase查询,后面这一查询步骤的时间几乎能够忽略,由于这是HBase最擅长的场景,交互图以下所示:

18a9ce6841b48994cc609a5f25651afe0e655de1

  • HBase检索能力扩展

cb04f5f353108d46b1afda4fc4bef950ed7e1424

图片来源于HBase技术社区-HBase应用实践专场-HBase for Solr

 

总结

 

最后,对几种方案总结以下(sharding column简称为sc):

- 单个sc 多个sc sc+es sc+es+HBase
适用场景 单一 通常 比较普遍 很是普遍
查询及时性 及时 及时 比较及时 比较及时
存储能力 通常 通常 较大 海量
代码成本 很小 较大 通常 通常
架构复杂度 简单 通常 较难 很是复杂

 

总之,对于海量数据,且有必定的并发量的分库分表,毫不是引入某一个分库分表中间件就能解决问题,而是一项系统的工程。须要分析整个表相关的业务,让合适的中间件作它最擅长的事情。例若有sharding column的查询走分库分表,一些模糊查询,或者多个不固定条件筛选则走es,海量存储则交给HBase。

 

作了这么多事情后,后面还会有不少的工做要作,好比数据同步的一致性问题,还有运行一段时间后,某些表的数据量慢慢达到单表瓶颈,这时候还须要作冷数据迁移。总之,分库分表是一项很是复杂的系统工程。任何海量数据的处理,都不是简单的事情,作好战斗的准备吧

相关文章
相关标签/搜索