MySQL分表研究

MySQL分库分表是大厂常见的技术方案。

为什么要做分库分表呢?

  1. 分库有利于服务隔离与解耦,方便单独扩容和故障恢复。
  2. 分表可以解决大表的运维难题,提高并发性能。

分库大家比较容易理解,一般随着业务粒度的划分进行。

而分表的形式和用途多种多样,同样达到的目的和效果也各不相同。

常见的解决大表问题的方案是水平分表。本文重点介绍水平分表的方案以及扩容方式。

常见水平分表方式

连续分片

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

ID取模分片

一致性Hash算法

Snowflake 分片

Snowflake 是 Twitter 开源的分布式 ID 生成算法,其结果为 long(64bit) 的数值。
其特性是各节点无需协调、按时间大致有序、且整个集群各节点单不重复。
该数值的默认组成如下(符号位之外的三部分允许个性化调整):

Snowflake

分表多维度查询

比如,有一张订单表,订单表有100个字段,常用查询方式有两种:

  1. 根据用户id查所有的订单
  2. 根据商品id查所有的订单
  3. 根据订单id查询订单详情

如果根据用户ID取模的方式分表,这时候根据商品id查询数据可能会落在多个分表上。

同理,如果根据商品ID取模的方式分表,这时候根据用户id查询数据可能会落在多个分表上。

Mysql方案

把数据冗余两份,分别按用户id、商品id、订单来做分表,以空间换时间。

适合数据量可控,查询纬度最多两个且固定的业务。

构建宽表,使用Elasticsearch

ElasticSearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。它能很方便的使大量数据具有搜索、分析和探索的能力。
充分利用ElasticSearch的水平伸缩性,能使数据在生产环境变得更有价值

分表扩容

对于快速发展的业务来说,经常会遇到这样的问题,在设计分表的时候,数据量预估不准确,导致分表数量不够,后期需要扩容。

当然,扩容的要求是尽量少的数据迁移和改造。

常规方案

如果增加的节点数和扩容操作没有规划,那么绝大部分数据所属的分片都有变化,需要在分片间迁移:

  1. 预估迁移耗时,发布停服公告;
  2. 停服(用户无法使用服务),使用事先准备的迁移脚本,进行数据迁移;
  3. 修改为新的分片规则;
  4. 启动服务器。

免迁移扩容原理

  1. 单表架构

单表

采用双倍扩容策略,避免数据迁移。

扩容前每个节点的数据,有一半要迁移至一个新增节点中,对应关系比较简单。

具体操作如下(假设已有 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 修改配置

主要修改两处:

    a)数据库实例所在的机器做双虚ip,原来%2=0的库是虚ip0,现在增加一个虚ip00,%2=1的另一个库同理

    b)修改服务的配置(不管是在配置文件里,还是在配置中心),将2个库的数据库配置,改为4个库的数据库配置,修改的时候要注意旧库与辛苦的映射关系:

    %2=0的库,会变为%4=0与%4=2;

    %2=1的部分,会变为%4=1与%4=3;

这样修改是为了保证,拆分后依然能够路由到正确的数据。

2 reload配置,实例扩容

服务层reload配置,reload可能是这么几种方式:

a)比较原始的,重启服务,读新的配置文件

b)高级一点的,配置中心给服务发信号,重读配置文件,重新初始化数据库连接池

不管哪种方式,reload之后,数据库的实例扩容就完成了,原来是2个数据库实例提供服务,现在变为4个数据库实例提供服务,这个过程一般可以在秒级完成。

整个过程可以逐步重启,对服务的正确性和可用性完全没有影响:

a)即使%2寻库和%4寻库同时存在,也不影响数据的正确性,因为此时仍然是双主数据同步的

b)服务reload之前是不对外提供服务的,冗余的服务能够保证高可用

完成了实例的扩展,会发现每个数据库的数据量依然没有下降,所以第三个步骤还要做一些收尾工作。

3 收尾工作,数据收缩

有这些一些收尾工作:

a)把双虚ip修改回单虚ip

b)解除旧的双主同步,让成对库的数据不再同步增加

c)增加新的双主同步,保证高可用

d)删除掉冗余数据,例如:ip0里%4=2的数据全部干掉,只为%4=0的数据提供服务啦

这样下来,每个库的数据量就降为原来的一半,数据收缩完成。

总结

该帅气方案能够实现n库扩2n库的秒级、平滑扩容,增加数据库服务能力,降低单库一半的数据量,其核心原理是:成倍扩容,避免数据迁移。

迁移步骤:

(1)修改配置

(2)reload配置,实例扩容完成

(3)删除冗余数据等收尾工作,数据量收缩完成

参考文献

  1. 【干货】MySQL 分库分表及其平滑扩容方案
  2. 数据库秒级平滑扩容架构方案