声明

本篇文章除部分引用外,均为原创内容,如有雷同纯属巧合,引用转载请附上原文链接与声明。

阅读条件

读本篇文章需掌握数据库设计、系统架构基础等方面知识

注意

本文若包含部分下载内容,本着一站式阅读的想法,本站提供其对应软件的直接下载方式,但是由于带宽原因下载缓慢是必然的,建议读者去相关官网进行下载,若某些软件禁止三方传播,请在主页上通过联系作者的方式将相关项目进行取消。

关键词
  • 分库分表设计
  • 平滑扩容/缩容解决方案
  • 无锁化设计
  • 高并发系统
  • 读写分离架构方案
一、简介

在高并发的系统中,系统的TPS指标瓶颈的一般是来源于数据库。单机数据库的资源和处理能力有限。在高并发的分布式系统中,可采用分库分表突破单机局限。本文通过分库分表的相关概念、分片策略、平滑扩/缩容方案、读写分离架构方案进行介绍

二、分库分表概述

在业务量不大时,单库单表即可支撑。当数据量过大存储不下、或者并发量过大负荷不起时,就要考虑分库分表。分库分表的目的是将数据进行分散,分为若干个子集,称为逻辑分片,逻辑分片均对应有真实的物理分片,每个物理分片也可以被划分为多个逻辑分片。

2.1、分库分表相关术语
  • 读写分离: 不同的数据库,具有相同的数据,分别只负责数据的读和写;MySQL Master-Slave模式:Master负责写,Slave负责读
  • 分库:一个系统的多张数据表,存储到多个数据库实例中;
  • 分表: 对于一张多行(记录)多列(字段)的二维数据表,又分两种情形:
    (1) 垂直分表: 竖向切分,不同分表存储不同的字段,可以把不常用或者大容量、或者不同业务的字段拆分出去。
    (2) 水平分表: 横向切分,按照特定分片算法,不同分表存储不同的记录。
2.2 真的要采用分库分表?

需要注意的是,分库分表会为数据库维护和业务逻辑带来一系列复杂性,除非预估的业务量大到万不得已,切莫过度设计、过早优化。
规划期内的数据量和性能问题,尝试能否用下列方式解决:

  • 当前数据量:如果没有达到几百万,通常无需分库分表;
  • 数据量问题:增加磁盘、增加分库(不同的业务功能表,整表拆分至不同的数据库);
  • 性能问题:升级CPU/内存、读写分离、优化数据库系统配置、优化数据表/索引、优化 SQL、分区、数据表的垂直切分;
  • 如果仍未能奏效,才考虑最复杂的方案:数据表的水平切分。
2.3 分片策略(路由策略、sharding)
2.3.1 连续分片

根据特定字段(比如用户ID、订单时间)的范围,值在该区间的,划分到特定节点。
优点:集群扩容后,指定新的范围落在新节点即可,无需进行数据迁移。
缺点:如果按时间划分,数据热点分布不均(历史数冷当前数据热),导致节点负荷不均。数据规模膨胀时,分片策略需要快速进行调整适配

2.3.2 ID取模分片

优点:结合一致性哈希,虚拟节点等算法,平衡数据分布
缺点:扩容后需要迁移数据
举例:假设分库数为M,每个库的表数量为N,计算方法如下:

  • 首先计算总表个数:M * N = K
  • 计算数据所在所有表的index:id % K
  • 计算所在库索引:index / N
  • 计算所在库的表索引:index % N
三、系统扩容、缩容方案
3.1 分库数、分表数抉择

分表、分库的总数、redis库的总数、HashMap容量都是2的N次方,为什么?

  • 因为如果分表数量 M 是 2的N次方时,在定位数据所在表的取模运算可以变换为位预算: id % M = id & (M - 1),位运算在计算机中非常迅速的。
  • 在进行缩容扩容时,可以计算出影响的数据范围
3.2 常规方案
  • 预估迁移耗时,发布停服公告;
  • 停服(用户无法使用服务),使用事先准备的迁移脚本,进行数据迁移;
  • 修改为新的分片规则;
  • 启动服务器

该方案在互联网企业中基本上不可接受的

3.3 平滑方案

采用双倍扩容策略,避免数据迁移。扩容前每个节点的数据,有一半要迁移至一个新增节点中,对应关系比较简单。具体操作如下(假设已有 2 个节点 A/B,要双倍扩容至 A/A2/B/B2 这 4 个节点):

  • 无需停止应用服务器;
  • 新增两个数据库 A2/B2 作为从库,设置主从同步关系为:A=>A2、B=>B2,直至主从数据同步完毕,保持实时同步
  • 调整分片规则并使之生效:原 ID%2=0 => A 改为 ID%4=0 => A, ID%4=2 => A2;原 ID%2=1 => B 改为 ID%4=1 => B, ID%4=3 => B2。
  • 解除数据库实例的主从同步关系,并使之生效;
  • 此时,四个节点的数据都已完整,只是有冗余(多存了和自己配对的节点的那部分数据),择机清除即可(过后随时进行,不影响业务)。

扩容后,数据的索引变化。其次可以看出,每次扩容/缩容的数据变化是对取模范围值为1的部分才会发生数据迁移,这个特点在进行缩容时进行数据范围影响评估可以进行很好的应用。这也是选择2的N次方作为取模底数的一个原因,可以对数据的影响范围进行计算统计。

3.5 实际落地方案

在互联网企业中,数据库连接通常通过代理的方式对外提供,应用服务不与数据库直接进行连接。

常见代理比如:MyCat,部署一台MyCat代理服务器伪装成 MySQL 服务器,代理服务器负责与真实 MySQL 节点的对接,应用程序只和代理服务器对接。真实的数据库对业务系统是透明的。

在进行数据库扩展时,可以通过DBA配置数据表同步,并修改配置,实现JDBCUrl不变但是指向不同的真实物理数据库实例,做到业务系统不修改代码且无需上线实现平滑扩/缩容,这个过程可能会出现秒级影响(数据库代理与新物理库建立连接,该阶段对数据库的读写请求进行阻塞,等待连接切换完毕)
早期设计中,可以将系统的TPS承载能力按照实际业务量级所需要的2-3倍进行设计,此时设计的数据分片为逻辑分片,而不同的逻辑分片数据可以存储在相同的物理分片中;这样在早期发展中,可以将不同的逻辑分片进行分组存放,以进行实际的物理成本控制;随着业务的发展,需要更高的性能和容量支撑时,可以将存储在相同物理分片的逻辑分片数据迁出至新的物理数据库,从而提升系统性能。

比如:早期阶段系统高峰期只需要1万的TPS要求,在进行系统设计时可以直接按照2万,或者3万的TPS提前进行设计,假如单库可支撑每秒的修改为1000,那么至少需要将数据分为10个物理分片进行支撑,如果不考虑未来扩展,1个物理分片只对应1个逻辑分片,则只需要将数据拆分为10个逻辑分片,至此可以满足1万TPS需求。但是为了防止业务快速发展(参考疫情期间,各大互联网买药平台订单量量级几十倍的增长),在实际设计时,设计的系统容量应该按照需求的2-3倍设计,所以这里可以选择设计30个逻辑分片,在早期不需要太多性能时,可以将每3个逻辑分片划分在同一个数据库物理分片上,即底层还是10个物理分片,实际上数据库性能仍然只能支撑1万TPS,成本遍得到了控制;当需要扩展时,无需进行新的逻辑分片设计,仅仅只需要关注逻辑分片对应数据的物理分片数据迁移。且无需修改代码和上线操作,业务系统完全无感知。即在设计阶段,逻辑分片的数量往往大于物理分片数量。

四、常见高性能系统之读写分离架构
  • 写库支持水平扩展,当性能不足时,水平扩展底层数据库
  • 非在线实时查询,比如业务分页查询,则读取通过DTS同步数据的读库,这里一般会冗余查询所用的字段,以支持复杂的查询条件
  • 如果查询性能无法满足,可以通过增加索引、多级缓存等方式进行满足
  • 该实现可以灵活变动,从根本上解决掉连表问题等
五、分库分表所引入的问题
  • 分片所引起的分布式事务问题(引申:无共享型数据架构)
  • 跨分片连表、聚合、统计
  • 扩容、缩容所引起的数据迁移
六、引申
  • 银行最基本的转账业务采用分库分表会出现什么问题,如何解决?
  • 如何解决查询维度变化不停变化、面向完全不同的业务方(C端、B端等等)需求、性能及其吞吐量需求提供查询解决方案 ?
  • 推荐了解:无共享数据架构、无锁化设计、高性能系统设计
七、参考引用