(二十一)MySQL之高并发大流量情况下海量数据分库分表的正确姿势

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: 从最初开设《全解MySQL专栏》到现在,共计撰写了二十个大章节详细讲到了MySQL各方面的进阶技术点,从最初的数据库架构开始,到SQL执行流程、库表设计范式、索引机制与原理、事务与锁机制剖析、日志与内存详解、常用命令与高级特性、线上调优与故障排查.....,似乎涉及到了MySQL的方方面面。但到此为止就黔驴技穷了吗?答案并非如此,以《MySQL特性篇》为分割线,整个MySQL专栏从此会进入“高可用”阶段的分析,即从上篇之后会开启MySQL的新内容,主要讲述分布式、高可用、高性能方面的讲解。

引言

   从最初开设《全解MySQL专栏》到现在,共计撰写了二十个大章节详细讲到了MySQL各方面的进阶技术点,从最初的数据库架构开始,到SQL执行流程、库表设计范式、索引机制与原理、事务与锁机制剖析、日志与内存详解、常用命令与高级特性、线上调优与故障排查.....,似乎涉及到了MySQL的方方面面。但到此为止就黔驴技穷了吗?答案并非如此,以《MySQL特性篇》为分割线,整个MySQL专栏从此会进入“高可用”阶段的分析,即从上篇之后会开启MySQL的新内容,主要讲述分布式、高可用、高性能方面的讲解。

接下来的数据库专栏内容,主要会讲解不同高并发场景下的MySQL架构设计方案,也包括对于各类大流量/大数据该如何优雅的处理,也包括架构调整后带来的后患又该如何解决?其中内容会涵盖库内分表、主从复制、读写分离、双主热备、垂直分库、水平集群、分库分表实践、分布式事务、分布式ID、数据一致性探讨......等内容。

   话归正题,分库分表这个概念基本上碰过数据库的小伙伴都有听说过,但很多小伙伴对这块具体该如何落地并不清楚,因此接下来这篇会先阐述MySQL分库分表的方法论,以及详细讲解分库分表后产生的后患问题,但在此之前先送上一句话,请牢记:

不要为了分库分表而分库分表!!引入SOA架构中的一句话:架构不是一蹶而起的,而是慢慢演进的。只有真正需要分库分表来解决问题时,才去真正的做拆分,否则会导致很多不必要的麻烦产生,这点在《阿里Java开发规范手册》中有明确的写出:
001.png

一、为什么需要分库分表?

   在讲为什么需要分库分表之前,咱们先来讲一个故事:在很久很久之前有一位名唤竹子的美男子,最初由于年幼并未娶妻生子,因此出行时都只需要一匹马来拉车,但随着年龄渐长,慢慢的开始娶妻纳妾,每次出行时的人数也会直线增长,而之前负责拉车的那匹老马却苦不堪言,因为随着日子一天一天的过,自身的压力也随之增加,终于有一天,老马扛不住了,累到在了大街上。

随即竹子也慢慢发现了这个问题,由于每次出行的人口越来越多,因此老马的能力无法满足出行需求,这时竹子为了解决出行问题,所以花费重金托人从西域购入了一匹身强体壮的汗血宝马,以此来寻求解决所遇到的困扰。

当商人将名贵的汗血宝马交给竹子时,这匹马的确比之前的老马能力强太多太多了,拖动一辆承载几人的马车完全不在话下,同时竹子有了之前的前车之鉴,因此对其也格外看重,每天都吩咐人给宝马喂好料、做保养......,但好景不长,随着时间推移,男人三妻四妾放在当时也并非罕事,所以这时这匹来自西域的汗血宝马也对此无能为力,慢慢的出现乏力的情况。

此时竹子也再次察觉到了这个情况,于是再次找到当初帮忙购置汗血宝马的商人,想要再花重金买入一匹能力更强、体力更盛的千里马!但商人却道:“目前你手中的汗血宝马已属世间极品,想要找到比它更强且能代替它的少之又少,贵客您这需求恐怕老夫是难以完成咯”!

这时问题似乎陷入僵局,但随之大行商便道:“虽然我无法替您寻找到更为优良的马匹,但我有一个万全之策能解你燃眉之急”!那这个完全之策到底是什么呢?此时商人口中缓缓道出:“当一匹马无法解决你的出行问题时,与其寻找更好的马匹,为何不选择用更多的马匹来拉车呢”?

此时竹子一拍大腿,立马称赞道:所言极是,言之有理,于是大手一挥立马又购置了六匹良马,与之前的两匹旧马,组成了八匹马拉车的马队,自从之后,竹子再也没有遇到过出行问题。

002.png

在上面这个故事中,大家应该能够感受出来,当单匹马无法拉动马车时,不要试图找到一匹更好的马来代替,而是应该选择使用多匹马来拉车!这个故事的内在意义放在编程中同样如此,对一个节点做性能优化、升级硬件配置就是再试图寻找一匹更强壮的马,但一匹马的力量再强也是有限的,所以这时选择使用更多的马匹(服务器/节点)来解决问题才是王道!

编程里面有句话叫做:加一台服务器的收益胜过千万次调优,毕竟机器数量才是真理!

那么接着咱们也回到问题本身,来一起聊一聊MySQL为什么需要分库分表?

1.1、请求数太高

   在高并发情况下,大量读写请求落入数据库处理,最终会导致数据库的活跃连接数增加,进而逼近甚至达到数据库可承载活跃连接数的阈值。在业务Service层来看就是,可用数据库连接锐减甚至无连接可用,接下来面临的就是并发量急剧增加、吞吐量严重下降、连接出现异常、数据库时常宕机、系统经常崩溃一系列后患问题。

1.2、数据查询慢

  • 一、单表或单库数据量过大,导致数据检索的效率直线降低。
  • 二、单库整体并发连接数接近系统阈值,从而导致此请求获取不到连接数,一直处于等待获取连接的状态。
  • 三、已经获取但由于并发过高导致CPU被打满,就算SQL所查询的表数据行很少,也同样因为没有CPU资源无法执行,所以一直处于阻塞状态,最终出现查询过慢的现象。

1.3、数据量太大

  • ①当一个库的数据存储量太大时,就算每张表的并发数不多,但是因为是海量数据,单库中存在大量的数据表,每张表都有一部分并发请求,导致最终单库的连接数阈值成为数据库的瓶颈。
  • ②当一张表数据太多时,导致单表查询速度严重下降,虽然InnoDB存储引擎的表允许的最大行数为10亿,但是如果一张表的数据行记录达到上亿级别,那就算通过索引去查询一条数据,它也需要至少经过上十次到几十次磁盘IO,从而导致单表查询速度直线下降;一般一张表的数据行数在800~1200W左右最合适。

1.4、单体架构的通病

单库中某张表遇到问题需要修复时,会影响了整个库中所有数据,因为有些严重的情况下需要停机优化后重新上线,这时其它一些没有出现问题的表,也会因此受到影响。

这就好比团队中一个人没完成好工作,所以导致整个团队一起陪同加班,这无疑很令人糟心。

1.5、MySQL数据库瓶颈

上面聊到的各类问题,本质上都是一些数据库瓶颈,一般程序的性能瓶颈都源自于硬件问题,而问题归根到底都属于IO、CPU瓶颈,接下来聊一聊IO、CPU瓶颈可以细分成哪些呢?

2.1、IO瓶颈

IO瓶颈主要分为两方面,一方面是磁盘IO瓶颈,另一方面则是网络IO瓶颈,具体如下:

磁盘IO瓶颈

①在之前《MySQL内存篇》中曾详细讲到过,MySQL为了提升读写性能,通常都会将一些经常使用的热点数据放入缓冲区,避免每次读写请求都走磁盘IO的方式去操作数据,但当整库数据数据太多时,可能会出现大量的热点数据,此时内存缓冲区中又无法全部放下,因此会导致大量的读写请求产生磁盘IO,通过读写磁盘的方式去完成数据读写,从而导致查询速度下降。

②一次查询数据的过程中,由于涉及到的数据过多,导致无法全部在内存中完成数据检索,如分组、排序、关联查询等场景,内存中相应的缓冲区无法载入要操作的全部数据,因此只能通过分批的方式处理数据,此时又需要经过大量磁盘IO后才得到最终数据集。

这种情况在单表查询时也存在,如果单表中字段过多,导致每一行数据的体积都比较大,因此会超出MySQL磁盘IO每次读取16KB的这个限制,因而也会出现检索单表数据时,一条数据就需要经过多次IO才能拿完。

简单来说磁盘IO瓶颈有两种情况,一种是磁盘IO次数过多,导致IO利用率持续居高不下,另一种情况就是每次读取数据都超出单次IO的最大限制,因此会引发多次IO,从而又演变成第一种情况。

网络IO瓶颈

当一个请求的生成的SQL语句执行后,由于这条语句得到的结果集数据太多,从而会导致相应时的数据包体积过大,这时如果网络带宽不够,就会出现传输过慢的问题,因为底层需要对大的数据包做拆包,然后分批返回,这时也会阻塞其它读写数据的请求,网络带宽就会成为新的瓶颈。

其实网络IO瓶颈的情况也比较好理解,网络带宽就好比一条马路,由于一条SQL返回的结果集过大,因此装载数据包的卡车远远超出了马路的宽度,这时就需要将数据拆分到多辆能通行的小卡车装载,但虽然这样做能够让数据包“变窄”成功返回,但因此也会造成这个大数据包会变长,从而占满整个带宽通道,因此其它网络数据包需要等这个大包传输完成后,才能继续通行。

2.2、CPU瓶颈

上面简单的了解IO瓶颈后,再来看看机器本身的CPU瓶颈,CPU瓶颈也会分为两种,一种是运算密集型瓶颈,另一种则是阻塞密集型瓶颈,但想要弄清楚这两种瓶颈,咱们首先得理解CPU的工作原理。

CPU工作原理

有深入研究过多线程编程的小伙伴,对于CPU的工作原理应该并不陌生,线程是操作系统最小的执行单元,因此CPU工作时本质上就是以线程作为载体,然后执行线程任务中的一条条指令,一般情况下单核CPU在同一时刻只能够支撑单挑线程运行,但随着2002年超线程技术的发布后,如今的CPU通常具备两组ALU执行单元,也就是大家常听到的单核双线程、四核八线程.....

因为超线程技术的存在,所以一核CPU在同一时刻可支撑两条线程一起运行,这种结构的CPU在现在也被称为大核架构,即一核心具备两逻辑处理器。而传统的单核单线程的CPU则被称为小核架构,目前最新的12/13CPU还有大小核异构的架构。

但无论是小核、大核、大小核架构的CPU,本质上在机器运行期间,往往整个系统内所有程序的线程数,加起来之后都会远超于CPU核数,如下:

003.png

那在这种情况下是如何工作的呢?相信有相关知识储备的小伙伴第一时间就会想到一个词汇:时间片切换执行,也就是有限的CPU核心会在所有线程之前来回切换,以此来确保系统和程序的正常运转。

其中的原理简单理解起来也并不难,因为每条线程的指令在执行时都需要数据作为基础,因此CPU在执行某条指令后,执行新指令时需要加载数据,此时会去载入数据,而操作系统这时就会将CPU资源切换给其它的线程执行,等这条线程的数据准备好了再切换回来。

浅显层面的原理如上,再深入一些会涉及总线、I/O设备原理、活跃进程算法、资源切换原理......等一系列技术了,这里先就此打住,我们只需要了解到这里就够用啦!接着再回去聊聊所说的两种CPU瓶颈。

运算密集型瓶颈

用户请求生成的SQL语句中包含大量join联表查询、group by分组查询、order by排序等之类的聚合操作,同时这些操作要基于特别大的数据集做运算,导致执行时消耗大量CPU资源,CPU占用率直达100%+,因此无法再给其它线程提供执行所需的CPU资源。

阻塞密集型瓶颈

一张或多张表的数据量特别大,此时基于这些大表做数据检索时,需要扫描的数据行太多,虽然这些SQL语句不会大量消耗CPU资源,但由于数据量过大,会导致长时间占用CPU资源,从而造成其它线程无法获取CPU资源执行。

上面聊到的两种CPU瓶颈中,一种属于大量运算导致CPU资源耗尽,一种属于大表检索导致长时间占用CPU资源,两种情况都会导致CPU遇到瓶颈,从而无法给其它线程提供运行所需的资源。有人也许会说,似乎这是SQL不合理导致的呀?其实不然,因为往往很多正常的SQL也会出现大量消耗CPU、或检索大表数据长时间占用CPU的情况。

无论是IO瓶颈,还是CPU瓶颈,都可以通过升级硬件配置的方式来解决,比如升级磁盘材质、加大网络带宽、增多CPU核数等,但前面讲到过,这种方式面对高并发大流量的冲击,治标不治本!如果客户端流量过大,这时不应该再试图寻找更强壮的马来代替,而是应该选择多匹马的方案来解决。

其实这也是一种节省成本的做法,无限制升级硬件也不是长久之道,而且越到后期,配置越高的硬件成本越高,四颗八核十六线程的CPU,成本价可能还会比一颗32Core 64ThreadCPU要便宜。

1.6、再聊为何需要分库分表

其实不管是并发过高、或访问变慢、亦或数据量过大,本质上都属于数据库遭遇到了瓶颈,但只不过根据情况不同,分为不同类型的数据库瓶颈,但是最终对于客户端而言,就是数据库不可用了或者变慢了。

而导致数据库出现此类问题的原因,实则就是随着业务的发展,系统的数据不断增多、用户量不断增长、并发量不断变大,因此对于数据再进行CRUD操作的开销也会越来越大,再加上物理服务器的CPU、磁盘、内存、IO等资源有限,最终也会限制数据库所能承载的最大数据量、数据处理能力。

当出现上述这类问题,并且无法通过升级硬件、版本、调优等手段解决时,或者只能临时解决,却无法保障未来业务增长的可用性时,此刻就需要合理的设计数据库架构来满足不断增长的业务,这就是分库分表诞生的初衷,目的就是为了避免单库由于压力过高,导致出现之前所说的一系列问题,合理的设计架构能最大限度上提高数据库的整体吞吐量。

下面为了能够更好的讲透彻分库分表的方法论,我将以一个真实案例给大家阐述分库分表的架构演进过程。

二、传统单库架构到分库分表的演进史

   早些年我司新开一条业务线切入金融领域,最开始的因为担心风险过大,所以并未投入太多的成本,处于一个试错阶段,最初就把所有业务都怼入一个war包,所有业务共享一个库资源,结构大致如下:

004.png

而在当时那段时间,金融领域快速发展,慢慢的,Java搭建的金融核心系统开始出现响应变慢,甚至时不时宕机,部署整个金融核心系统的单台Tomcat很快遇到了瓶颈,后来实在因为Tomcat三天俩头宕机重启,迫于无奈开始了业务架构的改进,如下:

005.png

因为当时考虑到业务发展速度,并没有使用NginxTomcat进行横向拓展做水平集群,因为如果仅仅只是通过Nginx来做,可能以后还是需要对架构进行升级,进一步按业务拆分成分布式系统,所以经讨论后一致决定直接引入分布式架构对系统进行改造。

经过改造后的业务架构,Java应用这边的确可以抗住每天的流量,但当时因为在做Java程序架构升级的时候,只引入了Redis、MQ降低数据库并发,并没有去对数据库做太多的拓展,因此当时还是所有业务共享一个库。

随着时间的慢慢推移,虽然用MQ、Redis做了流量的削峰,但是也挡不住当时的流量请求,做过金融业务的小伙伴应该清楚,它不像其他业务领域中读多写少,金融业务中读多写也多,同时还需要每日对账、跑批、统计报表.....,因此对数据库的读写操作相当多,而金融业务又要求数据实时性,所以很多操作无法走MQ异步完成,也不能放入Redis做数据缓存,Why

好比拿股市中最基本的买入卖出为例,原本客户看到的是5$一股,然后用户选择了买入,因为数据放在缓存里没有及时更新,结果最新价格成了10$一股,此时用户买入10000$,按用户的预估应该是会买入2000股左右,结果最终买成了1000股,平白无故导致用户追涨。
也包括大量的写操作也无法走MQ异步完成,比如用户以5$的成本价买入,现在看到了最新的价格为10$一股,然后选择了全仓卖出,这时你将卖出操作发给了MQ异步执行,结果MQ中的卖出消息并未立马被消费,而是到了一小时后价格降到了2.5$一股时,才真正被消费,这回导致原本用户能赚100点,最后反变为倒亏50点。

虽然当时手上的项目并非交易所类型的金融业务,但无论是哪类金融业务,基本上对数据的实时性要求特别高,所以MQ、Redis基本上只能分担很小一部分的流量,其它大部分的流量依旧会需要落库处理。也正因如此,最终数据库成了整个系统的瓶颈口,为了去解决这个问题,最终选用服务独享库的方案进行升级(也就是后续要说的垂直分库模式),如下:

006.png

而数据库这边经过拆分之后,相较于之前的单库架构,整个数据库系统的稳定性和可用性明显得到改善,但由于某些库是经常需要被访问到的(资金库、信审库、后台库),所以这些核心库以单节点方式去承载流量还是显得有点吃力(吞吐量下降、响应速度变慢),最终又对核心业务库进行横向扩容,架构如下:

007.png

最终,根据服务不同的业务规模,拆成了规模不同、业务不同的库,但是这其中的拆分规则到底是什么呢?以及拆分的依据又是啥?接着一起来聊一聊!

三、分库分表正确的拆分手段

   现在你的手里有一个西瓜,吃的时候切法有两种,一种是以垂直方向竖切,另一种是以水平方向横切,如下:

008.png

这种切割方式在分库分表中也存在,分库分表的拆分规则也可分为:水平、垂直 两个维度。

但水平、垂直该怎么拆?什么场景下拆?拆完会出现的问题又该怎么去解决呢?那么接着来一步步分析到底怎么拆,拆完的问题怎么去解决~

注意:分库、分表是两个概念,两者并不是同一个名词,所以这里需要牢记!按拆分的粒度来排序,共计可分为四种方案:垂直分表、水平分表、垂直分库、水平分库

3.1、不同场景下的分表方案

   分表大多是在单表字段过多或数据过多的场景下,会选择的一种优化方案,当一个表字段过多时,应当考虑垂直分表方案,将多余的字段拆分到不同的表中存储。当一个表的数据过多时,或者数据增长速率过快时,应当考虑通过水平分表方案,来降低单表的数据行数。

3.1.1、垂直分表:结构不同,数据不同(表级别)

当一张表由于字段过多时,会导致表中每行数据的体积变大,而之前不仅一次聊到过:单行数据过大带来的后患,一方面会导致磁盘IO次数增多,影响数据的读写效率;同时另一方面结果集响应时还会占用大量网络带宽,影响数据的传输效率;再从内存维度来看,单行数据越大,缓冲区中能放下的热点数据页会越少,当读写操作无法在内存中定位到相应的数据页,从而又会产生大量的磁盘IO

从上述的几点原因可明显感受到,当单表的字段数量过多时,会导致数据检索效率变低、网络响应速度变慢、数据库吞吐量下降等问题,面对于这种场景时,就可以考虑垂直分表。

例:现在有一张表,总共43个字段,但是对于程序来说,一般经常使用的字段不过其中的十余个,而这些经常使用的字段则被称之为热点字段,假设此时这张表中的热点字段为18个,剩下的冷字段为25个,那么我们就可以根据冷热字段来对表进行拆分,如下:

009.png

对字段过多的表做了垂直拆分后,这时就能很好的控制表中单行数据的体积,从而能够让经常使用的字段数据更快的被访问、更快的返回。不过在做垂直拆分时,记得在冷字段的表中多加一个列,作为热字段表的外键映射,保证在需要用到冷数据时也能找到。

对于这种垂直分表的场景在很多业务中都有实现,如用户数据会分为users、user_infos,订单数据会分为order、order_info......。所谓的垂直分表其实和之前《库表设计篇》中聊到的范式设计,大致含义是类似的,如果表结构是按照数据库三范式设计的,基本上也无需考虑做垂直分表。

经过垂直拆分后的两张或多张表,各自之间的表结构不同,并且各自存储的数据也不同,这是垂直分表后的特性,以上述例子来说,热点字段表会存储热数据,冷字段表会存储冷数据,两张表的拼接起来后会组成完整的数据。

3.1.2、水平分表:结构相同,数据不同(表级别)

前面聊到了字段过多对读写数据时的影响,接着再来看看数据过多时会导致的负面影响,虽然数据库中有索引机制,能够确保单表在海量数据的基础上,检索数据的效率依旧可观,但随着数据不断增长,当达到千万级别时,就会出现明显的查询效率下降的问题。

这里所谓的查询效率下降并非指单表的简单查询语句,而是指一些复杂的SQL语句,毕竟线上往往很多需求,都要经过复杂的SQL运算后才能得到数据,比如多张表联查再跟了一堆分组、排序、过滤、函数处理.....语句,这种情况下再基于这种大表查询,就算走了索引,效率也不会太高,因为其中要涉及到大量数据的处理,因此面对这种情况,就可以对表进行水平拆分。

例:现在有一张表,里面有三千万条数据记录,当基于该表去执行一条在索引上的复杂SQL时,也需要一定时间,至少会比1000万的数据表慢了好几倍,此时可以把这张3000W的表,拆为三张1000W的表,如下:

010.png

对一张大表做了水平分表之后,咱们能够很好的控制单表的数据行数,3000W条数据的表和1000W条数据的表,查询速度其实不仅仅只是3倍的差距,数据过了千万级别时,数据量每向上增长一个量级,查询的开销也会呈直线性增长,因此做水平分表时,一般要求控制在500-1200W之间为一张表。

阿里内部的单表数据量大概控制在500~600W一张,因为这个数据量级,就算使用分布式策略生成的分布式ID作为主键,也能够很好的把索引树高控制在3~5以内,也就意味着最多三到五次磁盘IO就一定能得到数据,从而将单表的查询性能控制在最佳范围内。

水平拆分之后的两张或多张表,每张表的表、索引等结构完全相同,各表之间不同的地方在于数据,每张表中会存储不同范围的数据。不过拆分之后的水平表究竟会存储哪个范围的数据,这要根据水平分表的策略来决定,你可以按ID来以数据行分表,也可以按日期来以周、月、季、年.......分表。

PS:诸位看下来应该会发现,水平分表的方案和之前聊到过的《MySQL表分区技术》十分类似,但这不意味着表分区可以代替水平分表,答案恰恰相反,一般要用表分区的场景中可以选择水平分表的方案来代替。

3.1.3、分表方案总结

分表方案主要是针对于单表字段过多或数据过多的情况去做的,通过垂直、水平分表的手段,能够很好解决单表由于字段、数据量过多产生的一系列负面影响,但无论是垂直分表还是水平分表,都必须建立在单库压力不高,但是单表性能不够的情况下进行的,因为它们都属于库内分表。

如果是数据库整体压力都很大的情况,从而导致的查询效率低下,那不管再怎么做分表也无济于事,毕竟连流量入口都出现了拥塞,自然分表也无法解决问题,所以分表操作只建立在单库压力不高,但是单表查询效率低下的情况适用。

好比把数据库比作一个游乐园,而表则可以比作里面的一个个娱乐项目,由于某些娱乐项目比较火爆,因此可以对同一类型的项目多开几个,从而解决热点项目顾客要排队很久才能玩的问题。但如果是整个游乐园的人流量都非常大,每个项目都有大量顾客排队,这时再去对内部的娱乐项目作拓展,这种方式是行不通的,毕竟游乐园的大门就那么大,游客连进大门都要排队,再在内部作项目拓展显然无济于事。

3.2、不同场景下的分库方案

   经过前面的分表总结后可以得知:如果是因为库级别的压力较大,这时就需要考虑分库方案,而不仅仅是分表方案,换到上面的例子中,当整个游乐园的人流量非常大时,应该考虑的是开分园,而并非是在内部作拓展。

分库和分表一样,也可以按垂直和水平两个维度来分,垂直分库本质上就是按业务分库,也就是现在分布式/微服务架构中,业务独享库的概念,而水平分库则是对同一个节点作横向拓展,也就是高可用集群的概念。

3.2.1、垂直分库:结构不同,数据不同(库级别)

当数据库使用单机的结构部署,在大流量/高并发情况下遇到瓶颈时,此时就可以考虑分库方案了,首先来聊聊垂直分库。

在项目开发过程中,一般为了方便团队分工合作和后续管理维护,通常都会对单个项目划分模块,按照业务属性的不同,会将一个大的项目拆分为不同的模块,同时每个业务模块也会在数据库中创建对应的表。

而所谓的垂直分库,就是根据业务属性的不同,将单库中具备同一业务属性的表,全部单独拧出来,放在一个单独的库中存储,也就按业务特性将大库拆分为多个业务功能单一的小库,每个小库只为对应的业务提供服务,这样能够让数据存储层的吞吐量呈几何倍增长。

例:以前面给出的金融项目来说,当单个库无法承载整个业务系统产生的流量压力时,比如此时单个数据库节点的QPS上限为2000,但业务高峰期抵达数据库的瞬时流量,造成了2W个并发请求,这时如果处理不当,数据库基本上会被这波瞬时流量打宕机。

对于前面所说的这种情况,就可以考虑根据业务属性拆分整个大库了,核心思想就是:既然单个节点扛不住,那就加机器用多个节点来抗,在客户端按照不同的业务属性,将过来的请求按照不同的业务特性做分流处理,如下:

011.png

原本之前单库时,无论是查询用户业务相关的SQL语句,还是放款/还款之类的SQL语句,不管三七二十一统统发往同一个数据库处理,全部都由这一个数据库节点提供数据支持,但按业务特性做了垂直分库后,用户相关的读写请求落入用户库,放款/还款之类的读写请求会落入资金库.....,这样就能很好的去应对单库面临的负载过高问题。

垂直分库后,每个库中存储的数据都不相同,因为是按照业务特性去将对应的表抽出去了组成新库,所以库结构也是不同的,用户库是由用户相关的表组成、信审库是由心生相关的表组成.......。

3.2.2、水平分库:结构相同,数据不同(库级别)

经过前面的垂直分库后,根据不同的业务类型,将访问压力分发到不同的库处理后,虽然在极大程度上提升了数据层的负荷能力,但如果某类业务的并发数依旧很高,比如经过前面的业务分流后,假设平台库需要承载5000的并发、信审库依旧需要承载1W的并发,这也远超出了单个数据库节点的处理瓶颈,最终可能还是会能把对应的数据库节点打宕机,所以此时可通过水平分库的方案,来提升某类业务库的抗并发吞吐量。如下:

012.png

通过水平拆分的方案,能够根据压力的不同,分配不同的机器数量,从而使得不同库的抗压性都能满足对应的业务需求,这也就类似于分布式/微服务项目中,对单个服务做集群保证高可用的策略。

水平分库是基于一个节点,然后直接横向拓展,这也就意味着同一业务的数据库,各节点之间的库结构完全相同,但每个节点中的数据是否相同,这就要看你自己去决定了,一般情况下都是不同的,也就是不同节点的库会存储不同范围的数据。

3.2.3、另类的分库方案

前面聊清楚了分库分表中经典的垂直分库和水平分库方案,但除开这两种之外,还有一些另类的分库方案,也就是指一些数据库的高可用方案,例如主从复制、读写分离、双主热备等方案。

主从方案:一般会搭建读写分离,写请求发往主节点处理,读请求发往从节点处理,从节点会完全同步主节点的数据,从而实现读写请求分开处理的效果,能够再一定程度上提升数据存储层整体的并发处理能力。同时当主机挂掉时,从机也能够在很快的时间内替换成主机,以此确保数据层的高可用。

多主方案:一般是双主方案,两台数据库节点之间互为主从,相互同步各自的数据,两台节点中都具备完整的数据,读写请求可以发给任意节点处理。相较于前面的主从读写分离架构,这种双主双写架构的灾备能力更强,因为当其中某个节点宕机时,另一个节点可以完全接替对方的流量,不存在从机切换成主机的时间开销,因此能够保证数据100%不丢失。

不过无论是主从、还是多主方案,本质上都存在木桶效应问题,因为这种分库方案中都会完全同步数据,当一个节点的数据存满时,会导致其他节点也不可用,对于这里的具体原因可参考《MySQL优化篇-架构优化》

四、分库分表总结

   分库方案能够在最大程度上提升数据存储层的性能,但一般在考虑选用分库方案时,应该先考虑使用主从、主主的方案,如果前面两种方案依旧无法提供系统所需的吞吐量,再考虑选择垂直分库方案,按照业务属性去划分库结构,最后才应该考虑选择水平分库方案(同时也要记得考虑数据的增长速率情况)。

那为什么需要遵循这个顺序呢?因为架构不能过度设计,选用主从、主主能够满足需求时,就选这两种方案,因为一方面能避免很多问题产生,同时实现起来也比较简单。同时先考虑垂直分库,再考虑水平分库,是因为水平分库可以建立在垂直分库的基础上,进一步对存储层作拓展,因此灵活性会更高,拓展性会更强。

同时最后再聊一下分库之后带来的好处:

  • ①能够得到最大的性能收益,吞吐量会随机器数量呈直线性增长。
  • ②能够最大程度上保障存储层的高可用,任意节点宕机都不会影响整体业务的运转。
  • ③具备相当强的容错率,当一个库中的结构存在问题需要重构时,无需将所有业务停机更新。
  • ④具备高稳定性,分库+配备完善的监控重启策略后,基本上能确保线上无需人工介入管理。

也就是说,分库方案能够让你的存储层真正达到高可用、高性能、高稳定的“三高”水准。

但要切记不能盲目的分库分表,分库分表前得先清楚性能瓶颈在哪里,然后根据业务以及瓶颈,遵循拆分规则的顺序做合理的拆分方案选择,因为分库分表虽然能带来很大的好处,但是同时也产生了一系列的问题需要去解决。

如果做了分库分表就一定要记住:既不能过度设计,也要考虑数据增长性,提前设计好扩容方案,以便于后续性能再次出现瓶颈时,能够基于现有架构进行优雅升级,一位优秀的开发/架构必须具备前瞻性

但关于如何设计出一套合理的扩容方案,也包括分库分表后究竟会产生哪些后患问题,这些问题产生后又该怎么解决,具体的内容可参考下篇:《分库分表后患问题一站式解决方案》。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3月前
|
存储 算法 关系型数据库
(二十二)全解MySQL之分库分表后带来的“副作用”一站式解决方案!
上篇《分库分表的正确姿势》中已经将分库分表的方法论全面阐述清楚了,总体看下来用一个字形容,那就是爽!尤其是分库分表技术能够让数据存储层真正成为三高架构,但前面爽是爽了,接着一起来看看分库分表后产生一系列的后患问题,注意我这里的用词,是一系列而不是几个,也就是分库分表虽然好,但你要解决的问题是海量的。
360 3
|
2月前
|
存储 SQL 关系型数据库
一篇文章搞懂MySQL的分库分表,从拆分场景、目标评估、拆分方案、不停机迁移、一致性补偿等方面详细阐述MySQL数据库的分库分表方案
MySQL如何进行分库分表、数据迁移?从相关概念、使用场景、拆分方式、分表字段选择、数据一致性校验等角度阐述MySQL数据库的分库分表方案。
402 15
一篇文章搞懂MySQL的分库分表,从拆分场景、目标评估、拆分方案、不停机迁移、一致性补偿等方面详细阐述MySQL数据库的分库分表方案
|
6月前
|
NoSQL 关系型数据库 MySQL
实时计算 Flink版操作报错之同步MySQL分库分表500张表报连接超时,是什么原因
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
3月前
|
安全 关系型数据库 MySQL
揭秘MySQL海量数据迁移终极秘籍:从逻辑备份到物理复制,解锁大数据迁移的高效与安全之道
【8月更文挑战第2天】MySQL数据量很大的数据库迁移最优方案
652 17
|
3月前
|
SQL 算法 Java
(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!
前面《MySQL主从原理篇》、《MySQL主从实践篇》两章中聊明白了MySQL主备读写分离、多主多写热备等方案,但如果这些高可用架构依旧无法满足业务规模,或业务增长的需要,此时就需要考虑选用分库分表架构。
2500 4
|
4月前
|
SQL 关系型数据库 MySQL
(八)MySQL锁机制:高并发场景下该如何保证数据读写的安全性?
锁!这个词汇在编程中出现的次数尤为频繁,几乎主流的编程语言都会具备完善的锁机制,在数据库中也并不例外,为什么呢?这里牵扯到一个关键词:高并发,由于现在的计算机领域几乎都是多核机器,因此再编写单线程的应用自然无法将机器性能发挥到最大,想要让程序的并发性越高,多线程技术自然就呼之欲出,多线程技术一方面能充分压榨CPU资源,另一方面也能提升程序的并发支持性。
388 3
|
4月前
|
算法 搜索推荐 NoSQL
面试题MySQL问题之分库分表后的富查询问题处理如何解决
面试题MySQL问题之分库分表后的富查询问题处理如何解决
49 3
|
4月前
|
算法 关系型数据库 MySQL
MySQL分库分表
【7月更文挑战第11天】分库分表策略涉及数据源、库和表的划分,如订单表可能分布于多层结构中。面试时,主键生成是关键点。自增主键在不分库分表时适用,但在分表场景下会导致冲突。例如,按`buyer_id % 2`分两张表,自增ID无法保证全局唯一。因此,需要全局唯一且能自增的ID,如雪花算法,兼顾性能和高并发需求。
39 1
|
3月前
|
SQL 关系型数据库 MySQL
(十六)MySQL调优篇:单机数据库如何在高并发场景下健步如飞?
在当前的IT开发行业中,系统访问量日涨、并发暴增、线上瓶颈等各种性能问题纷涌而至,性能优化成为了现时代中一个炙手可热的名词,无论是在开发、面试过程中,性能优化都是一个常谈常新的话题。而MySQL作为整个系统的后方大本营,由于是基于磁盘的原因,性能瓶颈往往也会随着流量增大而凸显出来。
447 0
|
4月前
|
SQL 关系型数据库 MySQL
mysql面试之分库分表总结
mysql面试之分库分表总结
76 0

热门文章

最新文章

  • 1
    高并发场景下,到底先更新缓存还是先更新数据库?
    67
  • 2
    Java面试题:解释Java NIO与BIO的区别,以及NIO的优势和应用场景。如何在高并发应用中实现NIO?
    75
  • 3
    Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
    68
  • 4
    Java面试题:如何实现一个线程安全的单例模式,并确保其在高并发环境下的内存管理效率?如何使用CyclicBarrier来实现一个多阶段的数据处理任务,确保所有阶段的数据一致性?
    62
  • 5
    Java面试题:结合建造者模式与内存优化,设计一个可扩展的高性能对象创建框架?利用多线程工具类与并发框架,实现一个高并发的分布式任务调度系统?设计一个高性能的实时事件通知系统
    55
  • 6
    Java面试题:假设你正在开发一个Java后端服务,该服务需要处理高并发的用户请求,并且对内存使用效率有严格的要求,在多线程环境下,如何确保共享资源的线程安全?
    69
  • 7
    在Java中实现高并发的数据访问控制
    42
  • 8
    使用Java构建一个高并发的网络服务
    29
  • 9
    微服务06----Eureka注册中心,微服务的两大服务,订单服务和用户服务,订单服务需要远程调用我们的用,户服务,消费者,如果环境改变,硬编码问题就会随之产生,为了应对高并发,我们可能会部署成一个集
    37
  • 10
    如何设计一个秒杀系统,(高并发高可用分布式集群)
    129