MySQL数据线上扩容方案

前言

什么是平滑扩容,举例子:

在项目初期,我们部署了四个数据库A、B、C、D,此时数据库的规模可以满足我们的业务需求

为了将数据做到平均分配,我们在Service服务层使用uid % 4进行取模分片,从而将数据平均分配到三个数据库

后期随着用户量的增加,用户产生的数据信息被源源不断的添加到数据库中,最终达到数据库的最佳存储容量

如果此时继续向数据库中新增数据,会导致数据库的CRUD等基本操作变慢,进而影响整个服务的响应速度

这时,我们需要增加新的节点,对数据库进行水平扩容,那么加入新的数据库E,数据库规模由4个变成5个

此时由于分片规则发生了变化(uid % 4 -> uid % 5),导致大部分的数据,无法命中原有的数据需要重新进行分配

要做大量的数据迁移处理,比如之前uid如果是4,取模后为0,分配到A库,新加入E库后,uid为4取模为4,分配到E库上

新增一个节点,大概会有90%的数据需要进行迁移,面临大量数据的压力,对服务造成极大的不稳定性

停机方案

  1. 发布公告:为了进行数据的重新拆分,在停止服务之前,我们需要提前通知用户,比如:我们的服务在yyyy-MM-dd进行升级,给您带来的不便敬请谅解

  2. 停止服务:关闭Service

  3. 离线数据迁移(拆分、重新分配数据):将旧库中的数据按照Service层的算法,将数据重新分散到新的数据库

  4. 数据校验:开发定一个程序对旧库和新库中的数据进行校验、比对

  5. 更改配置:修改Service层的配置算法,也就是将原来的 uid % 4 变为 uid % 5

  6. 恢复服务:重启Service服务

  7. 回滚预案:针对上述的每个步骤都要有数据回滚的方案,一旦某个环节(数据迁移、恢复服务等)执行失败,立刻回滚

停止服务后,能够保证迁移工作的正常进行,但是服务停止,对用户体验造成极大的影响,同时会有时间压力,必须在指定的时间内完成迁移

停写方案

  1. 支持读写分离:数据库支持读写分离,在扩容之前,每个数据库都提供了读写功能,数据重新分配的过程中,将每个数据库设置为只读状态,关闭写的功能
  2. 升级公告:为了进行数据的重新拆分,在停写之前,我们需要提前通知用户,比如:我们的服务会在yyyy-MM-dd进行升级,给您带来的不便敬请谅解。
  3. 中断写操作,隔离写数据源(或拦截返回统一提示):在Service层对所有的写请求进行拦截,统一返回提示信息,如:服务正在升级中,只对外提供读服务
  4. 数据同步处理:将旧库中的数据按照Service层的算法,将数据重新分配,迁移(复制数据)
  5. 数据校验:开发定制一个程序对旧库中的数据进行备份,使用备份的数据和重新分配后的数据进行校验,比对
  6. 更改配置:通过配置中心,修改Service层的配置算法,也就是将原来的 uid % 4 变为 uid % 5 ,这个过程不需要重启服务
  7. 恢复写操作:设置数据库恢复读写功能,去除Service层的拦截提示
  8. 数据清理:使用delete语句对冗余数据进行删除
  9. 回滚预案:针对上述的每个步骤都要有数据回滚预案,一旦某个环节(如:数据迁移等)执行失败,立刻进行回滚,重新再来

缺点:在数据的复制过程需要消耗大量的时间,停写时间太长,数据需要先复制,再清理冗余数据

双写方案

  1. 增加新库,按照现有的节点,增加对应的数量,然后进行数据迁移,为了避免增量影响,先断开著丛,再导入数据
  1. 进行增量同步:开启Cannal同步或者MQ同步增量数据,监听从节点数据库,再开启旧数据库集群的主从同步

    从节点收到数据后会通过Cannal服务,传递至新的DB节点

  1. 切换数据库集群:通过Nginx进行切流,将原来访问旧数据库集群的流量切换访问到新的数据库集群

    在切换过程中,如果出现Canal未同步,但已切换到新库的请求,可以通过定制程序,读取异常日志,进行自动修复或人工处理

    针对类似这种场景,最好在凌晨用户量小的时候,进行切换,减少异常数据的产生,同时为保障数据的一致性,需要做数据比对

平滑2N方案

线上数据库,为了保障其高可用,一般每台主库会配置一台从库,主库负责写入,从库负责读取

当需要扩容的时候,我们把A0 - Slave和B0 - Slave升级为新的主库节点,如此由2个分库变为4个分库

同时在上层的分片配置,做好映射,规则如下:

把uid%4=0和uid%4=2的数据分别分配到A和A0主库中

把uid%4=1和uid%4=3的数据分配到B和B0主库中

因为A和A0库的数据相同,B和B0数据相同,此时无需做数据迁移。只需调整变更一下分片配置即可,通过配置中心更新,不需要重启。

由于之前uid%2的数据是分配在2个库里面,扩容之后需要分布到4个库中,但由于旧数据仍存在(uid%4=0的节点,还有一半uid%4=2的数据),所以需要对冗余数据做一次清理。这个清理,并不会影响线上数据的一致性,可以随时随地进行。

处理完成之后,为保证数据的高可用,以及将来下一步的扩容需求。可以为现有的主库再次分配一个从库。

相关推荐
lang201509282 分钟前
Spring Boot 入门:5分钟搭建Hello World
java·spring boot·后端
间彧1 小时前
Windows Server,如何使用WSFC+nginx实现集群故障转移
后端
间彧1 小时前
Nginx + Keepalived 实现高可用集群(Linux下)
后端
间彧1 小时前
在Kubernetes中如何部署高可用的Nginx Ingress Controller?
后端
间彧1 小时前
Ribbon负载均衡器和Nginx负载均衡器有什么区别
后端
间彧2 小时前
Nacos详解与项目实战
后端
间彧2 小时前
nginx、网关Gateway、Nacos、多个服务实例之间的数据链路详解
后端
间彧2 小时前
Nacos与Eureka在性能上有哪些具体差异?
后端
间彧2 小时前
详解Nacos健康状态监测机制
后端
间彧2 小时前
如何利用Nacos实现配置的灰度发布?
后端