架构设计01——数据库拆分实现数据库能力线性扩展

我们公司前几年的核心系统,平均80人左右开发至今已经8年多了,至今还在维护,在全国20多个省部署了上千个点的运行,运行六七年后数据量上来了,结果平均每天都要出现宕机情况,90%以上都是数据库的原因,客户对我们的满意度急剧下降,可见数据库性能前期如果不设计好,后来带来的问题真的是灾难性的,虽然近些年各种存储技术层出不穷,但关系型数据库还是各种业务系统的核心,这篇文章详细讲讲我们在数据库性能方面如何实现线性扩展。

一、读写分离

最典型的场景就是单一数据库存储全部数据,数据量少、并发量少的时候没问题,数据量和并发量上来了就出现了问题,由于写(增删改)操作会对数据库上锁,数据量大导致写入较慢,或写入操作较多时候,导致读操作阻塞时间较长,从而引起性能下降。最常见、最有效、也是最容易实施的方案就是读写分离,讲读操作和写操作分离。

读写分离有2个关键点,一个是数据复制延迟,一个是应用访问:

1、数据复制延迟,主数据库写入数据后通过复制实现和从数据库同步,期间一定会有延迟,比较快的也就秒级同步,1s延迟都算快的,数据量大甚至可能达到分钟级别,所以对实时性要求特别特别高的应用就要好好考虑了,小心出现刚注册完,登录时提示没注册的现象。

应对复制延迟也有很多方法,比如对实时性要求高的操作在主库上进行,实时要求不高的放到从库上,也就是部分读写分离;再比如从库读失败了再从主库读一次等。

2、应用访问的便捷性,原来一个数据源的时候,应用直接写sql执行就行了,现在数据库集群了,不可能让应用自行分辨去查询哪个数据库,这时候要加一层数据访问层了,一般有两种方式,简单点的是代码中直接写,比如用hibernate访问数据库,简单封装一下hibernate就行,或者用一些组件(如淘宝的TDDL)等,如下图:

复杂一点的,就是使用数据库中间件,数据库中间件是一套独立的系统,业务系统无需自行管理读取哪个库,正常发送sql执行就行了,中间件将sql路由到要执行的数据库上,不过通常中间件比较复杂,能不能符合自己的需求还要自行测试,如下图:

读写分离后,拓展了数据库对读的处理能力,整体上也大大提高了数据库的读写能力,而且由于库分离后,可以针对库做不同的优化,比如在写库上减少索引,在读库上增加索引等。读写分离比较适合读多写少的操作,随着访问量增大,读的库可以水平扩展,大大提高了读写操作的压力,但却没有分散存储的压力,当数据量达到千万或亿条时候,单台数据库存储就会成为瓶颈,写操作就会极慢,索引维护也会时间很长,备份恢复也会很耗时间等。

二、分库

数据量大了,最常见,最直接的办法就是拆分库表,将一个大库拆成多个小库,一个大表拆成多个小表,这样性能瓶颈就降下来了,拆分也是有很多原则和方法的,最常见的就是按业务拆分库,这个粒度通常也是比较大的,就是按一定规则(通常是按业务)将表分类,放入不同的库中:

最大的好处就是分散了存储和访问的压力,拆分后业务清晰,专库专用维护简单,按业务扩展也容易,缺点也有不少,其中有3个重要的要考虑

1、跨库join,这个基本是所有分库分表都会涉及到的,联表查询就要修改成逐个查询了;

2、事务问题,数据库拆分后,分布式事务是最头痛的问题,我见过很多团队因为这个问题难以解决,他们的拆分原则就是把有事务的放到一个库中。

3、维护复杂,成本较高,数据库多了,肯定比但数据库维护复杂,建议数据量上来了,或者随着业务发展再拆库,避免上来就拆,运维复杂。

三、垂直分表

垂直拆分后,解决了业务间瓶颈的问题,但是单库表数据量大带来的瓶颈还没有解决,那单业务或单表遇到数据量大的瓶颈如何解决?这就涉及到拆分表了,一般有两种拆分方式,垂直拆分和水平拆分:

垂直拆分,当一个表特别宽,字段特别多的时候,每次读写全部对磁盘IO较大,性能容易出现瓶颈,可以将表的字段按访问频率分分类,比如我们之前做的业务里面,当事人表有300多个字段,常用字段大约10个,就可以拆分成当事人主表,当事人扩展表。还有类似有很多论坛的设计也是,用户表通常也被拆成两个表,一个user,一个user_ext,90%查询user就够了,这样也大大提高了性能。

拆表原则:

1、长度较短,访问频率较高的属性尽量放在主表

2、字段较长,访问频率较低的属性放在子表

3、经常一起访问的属性,放在一个表里,避免join和跨表查询

4、如果实在属性过多,主表和扩展表都可以有多个,不限于一个

这里提到了跨表查询,当我们可以一个sql查询一个user的全部信息时,我们可以一个sql联表查询,也可以两个简单sql,每个sql查询一个表,在应用中合并数据,我们怎么选?

执行一个sql的方法是将压力扔给了数据库,让数据库进行运算,执行两个简单的sql,数据库压力较小,而且由于查询简单,很多缓存等都可以直接使用,速度快,但应用计算压力大些。如何取舍呢,就要看未来的业务扩展了,如果有拆库拆表的可能,有数据量极大的可能,那尽量执行简单sql,减少数据库压力,因为相比数据库的压力,应用的瓶颈是比较好解决的,而数据库的瓶颈解决会很复杂。

四、水平分表

业务对数据的操作主要集中在某些字段上,比较适合垂直分表,当业务对数据的操作在整个表层面较均匀分布,那就适合水平分表了。相比垂直拆分的复杂度,水平拆分复杂度就上了一个级别,水平拆分是按照某种规则把结构相同的数据划分到不同表或数据库里,这些库表都是完全同构的,比如我们的user表有1亿条数据,并发读写都是问题,经过测算放到64个数据库中比较合适,每个数据库150w条,我们根据一个规则,id取模64进行运算,平均分布到这64个库中,如下图:

这就是一个典型的水平拆分场景,水平拆分后,未来如果数据量变化较大,可以通过动态扩充数据库来支持性能的扩展,看似非常好,但也带来一个非常大的问题,就是应用访问的时候要考虑查询哪个数据库,要做一次运算,这显然给应用带来了极大的麻烦。通常的解决办法就是增加一层数据库中间件(和前面读写分离时提到的中间件一样),主要做SQL路由,优化,数据聚合等工作,如下图:

现在的开源数据库中间件有一些,不过或多或少都有些不足,好一点的执行sql都是并发执行,也就是如图中的3、4步骤并发执行,大大提高sql执行效率,不过这些中间件很多比较复杂,很多公司由于需求简单,也经常自行开发,这些不在此次讨论范畴。

我们继续说水平拆分,拆分依据要选择最适合的,能够与业务能够吻合才是最好的,常见的有根据范围、枚举、时间、取模、哈希、指定等很多方式。水平拆分也有很多原则:

1、拆分要尽可能平均,不均匀会产生访问热点问题,我之前遇到过根据省份划分的,结果有些省数据量极大,就产生了不平均问题,性能瓶颈没解决。

2、尽量减少事务边界,事务边界的意思是指尽量符合业务操作,如果拆分后,每个业务操作都要查询全部的表,大量的跨库join等操作,反而会导致性能下降,没起到拆分的效果。

这两点有时候是冲突的,很多时候我们要取舍,举个例子,最常见的用户、订单两个表,订单数据量大,我们要水平拆分,遵守拆分平均的原则,我们设计成id自增,哈希取模进行平均拆分,分布到1024张表中,如下图:

这时候问题来了,用户大部分的操作是根据用户id查询购买的订单信息,想想你在网上买东西,看的最多的就是“我的订单”吧,所以业务上有大量的sql都是全表扫描,如下图:

第③④步骤要查询全部的表,性能消耗非常严重,如果数据多的时候,第⑤步的时候聚合时间较长,对cpu、内存消耗较大。那有什么好办法吗?根据第三个原则,我们查询较多的情况是根据用户查询订单,那应该按照用户id进行取模分库,而不是根据订单id。但是仔细想想,如果根据用户id进行拆分,可能有些用户买得东西多,有些用户买的少,结果就是订单就不会均匀分布在这些表中,依然没解决问题,那这两个原则,我们如何平衡呢?通常来说我们以数据平均原则为主,优先考虑平均,因为解决查询多,全表扫描等问题比较容易,有很多方案,而解决数据不均匀问题相对困难。

五、异构索引表

以平均原则为主后,如何解决跨表join、全表扫描等的场景呢?比较典型的方案就是异构索引表。就是在按订单id分表存储的时候,再存储一份以用户id为主的表,但只存储到id层面,也就是做到索引。

简单来说,也就是两套水平拆分都有了,想查询哪个就查询哪个,不过这种对磁盘资源消耗较大,所以以订单分区为主,人员分区只存储常用的字段,如id等,查询的时候需要查询两次,如下图(注意图中的顺序,先执行步骤②,根据用户id查询订单id,再执行步骤⑤,根据订单id查询内容):

异构索引表的方式已经能解决90%以上的问题了,如果还不满足,就要考虑其他专门用来查询的方案了,如实时性要求不高可以用Hadoop,实时性要求高可以用内存数据库,HBase等,这些不在咱们讨论范围,不深入讲解。

据了解淘宝目前就是采用异构索引表的方式,不过他们不单单做了索引,而是完全两套表,一套以订单水平拆分,一套以用户水平拆分,这样应用查询起来一个sql即可,非常简单方便,不过代价就是数据同步的要求较高。异构索引表的同步也是一个问题,最好的方式有个数据同步服务,自动根据订单表信息同步索引表。

六、总结

做到数据库的水平拆分,基本上就能够实现数据库性能的线性扩展了,未来数据量再大,通过增加节点就能够达到。总结一下,我们从读写分离、分库、分表讨论到了简单解决分表后的一些典型跨库查询方案,如下图:

其实我们讨论的还是很粗的,只是从大体的架构方案层面进行了讨论,如果真去实现的话有大量的细节需要考虑,不是几篇文章能够说清楚的,这里只是提供了通用思路,让大家对数据库性能设计,分库分表,线性扩展有一个整体了解,具体什么场景适合什么分法,如何权衡利弊,可能就要依赖对业务的积累和长期磨练的经验了。

补充一下,我们如果做设计,不要上来就分库分表,分库分表其实是最复杂的方案,往往是随着数据量的提高而演进过来的,简单说一下遇到性能问题我的大概思路:
1、优先做硬件优化,例如从机械硬盘改成固态硬盘等,根据实际情况判断
2、数据库层面调优操作,例如调整缓存,增加索引,数据库的很多的参数可以调整
3、缓存和其他技术,如redis,mongdb等,减少数据库压力
4、程序与数据库表优化,重构,sql优化等
5、这些都不能优化性能的情况下,单表数据量千万以上,再考虑分库分表吧,也别上来分太多,逐渐扩大
6、一定要根据业务考虑技术,根据场景,大部分的场景不需要太高实时性,不需要那么强的一致性,我们都有很多可优化的地方,还有很多可用的新技术,拆库拆表如果搞不好通常伤敌一千,自损八百。

最后,恭喜你看完了一篇4000多字的文章,希望能引起你思考,祝你有收获。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,069评论 4 358
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,212评论 1 287
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 105,912评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,424评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,741评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,194评论 1 206
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,553评论 2 307
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,289评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,923评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,251评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,775评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,144评论 2 249
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,698评论 3 228
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,936评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,658评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,214评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,159评论 2 258

推荐阅读更多精彩内容

  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,783评论 2 89
  • 转 # https://www.cnblogs.com/easypass/archive/2010/12/ 08/...
    吕品㗊阅读 9,609评论 0 44
  • 需要原文的可以留下邮箱我给你发,这里的文章少了很多图,懒得网上粘啦 1数据库基础 1.1数据库定义 1)数据库(D...
    极简纯粹_阅读 7,308评论 0 46
  • 今天是母亲节,本来我打算晚上打通电话给妈妈,可是一大早却接到了妈妈的电话,依旧是平常的关切语气,她只知道今天是个平...
    晨光你好阅读 2,356评论 0 2
  • 加拿大西部的人对蒙特利尔有种种误解甚至偏见。例如:我们以前经常听说他们不讲英文。你用英文问路,他们用法文给你指路。...
    谜样男人阅读 305评论 0 0