123457
导读
上一篇文章《ShardingJdbc分库分表实战案例解析(上)》中我们初步介绍了使用ShardingJdbc实现订单数据分散存储的分库分表方法,在本篇文章中将重点介绍在不停服的情况下实现数据分片存储的在线扩容。具体将以如下两个常见的场景进行演示:1)、尚未进行分库分表的单库单表系统如何平稳的实施分库分表方案;2)、已经实施过分库分表方案的系统,由于数据量的持续增长导致原有分库分表不够用了,需要二次扩容的情况。
实施方案概述
在具体演示之前,我们先简单聊一聊关于分库分表数据迁移中,几种常见的思路,具体如下:
1)、停服迁移
停服迁移方案,是数据迁移方案中最常见、也是相对安全,但是也是最不能让人接受的方案。之所以说它安全,是因为停服之后就不会有新的数据写入,这样能保证数据迁移工作能够在一个相对稳定的环境中进行,因而能较大程度上避免迁移过程中产生数据不一致的问题。
但停服务方案会比较严重伤害用户体验、降低服务的可用性,而如果数据量较大迁移需要大量的时间的话,长时间的停服会严重影响业务,对于强调"9999"服务体验的互联网公司来说,停服方案绝对是让人不能接受的。
一般来说停服方案更适合于哪些非7X24小时,且对自身数据一致性要求非常高的系统,例如社保基金、银行核心系统等。当然,这也并不是说非停服方案,做不到数据迁移的绝对准确,仅仅在于这些系统出于管理、规则等非技术因素上的考虑是否能够容忍小概率的风险事件罢了。
停服迁移的流程,一般如下:
1、提前进行演练、预估停服时间,发布停服公告;
2、停服,通过事先准备好的数据迁移工具(一般为迁移脚本),按照新的数据分片规则,进行数据迁移;
3、核对迁移数据的准确性;
4、修改应用程序代码,切换数据分片读写规则,并完成测试验证;
5、启动服务,接入外部流量;
2)、升级从库方案
关于升级从库的方案,一般是针对线上数据库配置了主从同步结构的系统来说的。其具体思路是,当需要重新进行分库分表扩容时,可将现有从库直接升级成主库。例如原先分库分表结构是A、B两个分库为主库、A0、B0分别为A、B对应的从库,具体如下图所示:
假设此时如果需要扩容,新增两个分库的话,那么可以将A0、B0升级为主库,如此原先的两个分库将变为4个分库。与此同时,将上层服务的分片规则进行更改,将原先uid%2=0(原先存在A库)的数据分裂为uid%4=0和uid%=2的数据分别存储在A和A0上;同时将uid%2=1(原先存在B库)的数据分裂为uid%4=1和uid%=3的数据分别存储在B和B0上。而因为A0库和A库,B0库与B1库数据相同,所以这样就不需要进行数据迁移了,只需要变更服务的分片规则配置即可。之后结构如下:
而之前uid%2的数据分配在2个库中,此时分散到4个库,由于老数据还在,所以uid%4=2的数据还有一半存储在uid%4=0的分库中。因此还需要对冗余数据进行清理,但这类清理并不影响线上数据的一致性,可以随时随地进行。
处理完成后,为保证高可用,以及下一步扩容的需求,可以为现有主库再次分配一个从库,具体如下图所示:
升级从库方案的流程,一般如下:
1、修改服务数据分片配置,做好新库和老库的数据映射;
2、DBA协助完成从库升级为主库的数据库切换;
3、DBA解除现有数据库主从配置关系;
4、异步完成冗余数据的清理;
5、重新为新的数据节点搭建新的从库;
这种方案避免了由于数据迁移导致的不确定性,但也有其局限性。首先,现有数据库主从结构要能满足新的分库分表的规划;其次,这种方案的主要技术风险点被转移至DBA,实施起来可能会有较大阻力,毕竟DBA也不见得愿意背这个锅;最后,由于需要在线更改数据库的存储结构,可能也会出现意想不到的情况,而如果还存在多应用共享数据库实例情况的话,情况也会变得比较复杂。
3)、双写方案
双写方案是针对线上数据库迁移时使用的一种常见手段,而对于分库分表的扩容来说,也涉及到数据迁移,所以也可以通过双写来协助分库分表扩容的问题。
双写方案实际上同升级从库的原理类似,都是做"分裂扩容",从而减少直接数据迁移的规模降低数据不一致的风险,只是数据同步的方式不同。双写方案的核心步骤是:1)、是直接增加新的数据库,并在原有分片逻辑中增加写链接,同时写两份数据;2)、与此同时,通过工具(例如脚本、程序等)将原先老库中的历史数据逐步同步至新库,此时由于新库只有新增写入,应用上层其他逻辑还在老库之中,所以数据的迁移对其并无影响;3)、对迁移数据进行校验,由于是业务直接双写,所以新增数据的一致性是非常高的(但需要注意insert、update、delete操作都需要双更新操作);4)、完成数据迁移同步,并校验一致后就可以在应用上层根据老库的分裂方式重新修改分片配置规则了。以前面的例子为例,双写方案如下图所示:
如上图所示原先的A、B两个分库,其中uid%2=0的存放在A库,uid%2=1的存放在B库;增加新的数据库,其中写入A库是双写A0库,写入B库时双写B0库。
之后分别将A库的历史数据迁移至A0库;B库的历史数据迁移至B0库。最终确保A库与A0库的数据一致;B库与B0库的数据一致。具体如下图所示:
之后修改分片规则,确保原先uid%2=0存放在A库的数据,在分裂为uid%4=0和uid%4=2的情况下能分别存储在A库和A0库中;原先uid%2=1存放在B库的数据,在分裂为uid%4=1和uid%4=3的情况下分别存在到B库和B0库之中。具体如下图所示:
双写方案避免了像升级从库那样改变数据库结构的风险,更容易由开发人员自己控制,但双写方案需要侵入应用代码,并且最终需要完成数据迁移和冗余数据删除两个步骤,实施起来也不轻松。
那么到底有没有一个绝对完美的方案呢?
答案其实是没有!因为无论哪种方案都无法避免要迁移数据,即便像升级从库那样避免了数据迁移,也无法避免对冗余数据进行删除的额外操作。但我们可以在数据迁移手段和重新处理数据分片的方式上进行优化,目前在分库分表领域著名的开源项目ShardingSphere本质上其实就是在这两方面进行了优化,从而提供了一组解决方案工具集。
具体来说ShardingSphere是由"Sharding-JDBC+Sharding-Proxy+Sharding-Scaling"三个核心组件组成的分库分表开源解决方案,Sharding-JDBC在《ShardingJdbc分库分表实战案例解析(上)》中我们已经介绍过。而Sharding-Proxy+Sharding-Scaling则是专门用于设计处理分库分表扩容数据迁移问题的组件,其运行原理如下:
如上图所示,在ShardingSphere的解决方案中,当需要对Service(旧)服务进行分库分表扩容时,我们可以先部署Sharding-Scaling+Sharding-Proxy组件进行数据迁移+数据分片预处理。具体来说步骤如下:
1)、在Sharding-Proxy中按照扩容方案配置好分片规则,启动服务并提供JDBC协议连接机制,此时通过Sharding-Proxy连接写入的数据会按照新的分片规则进行数据存储;
2)、部署Sharding-Scaling服务,并通过HTTP接口的形式,向其发送数据迁移任务配置(配置数据中有需要迁移的数据库连接串,也有Sharding-Proxy的数据连接串);
3)、启动Sharding-Scaling迁移任务后,Sharding-Scaling将根据目标数据源的Binlog日志变化,读取后重新发送至Sharding-Proxy进行分片数据的重新处理;
4)、当Sharding-Scaling迁移数据任务完成,检查数据迁移结果,如果没有问题,则可以修改Service(新)服务的数据分片规则,并完成Service(旧)服务的替换;
5)、确认扩容无误后,停止Sharding-Proxy+Sharding-Scaling服务;
6)、异步完成冗余数据的清理(目前ShardingSphere还不支持数据迁移后自动完成冗余数据的清理,所以需要自己根据数据分裂规则,编写清除脚本);
上述过程完全自动,在完成数据迁移及重新分片前,旧服务保持持续服务,不会对线上造成影响;此外由于Sharding-Scaling一直处于监听目标数据源Binlog日志状态,所以即便在服务切换过程中,旧Service服务仍有数据写入,也会自动被Sharding-Proxy重新分片处理,所以不用担心会出现不一致。
此外Sharding-Scaling+Sharding-Proxy通信方式采用的是JDBC连接原生连接方式以及基于Binlog日志的同步方案,所以在迁移效率上也是有保证的。接下来将基于Sharding-Scaling+Sharding-Proxy方案具体演示在不停服的情况下进行分库分表在线扩容。
ShardingSphere分库分表在线扩容
还是以上一篇文章中的订单分库分表存储为例,将其原有的分库分表规划:1)、数据库节点2个(ds0、ds1);2)、每个库的分表数为32张表(0~31)。扩容为:1)、数据库节点4个(ds0、ds1、ds2、ds3);2)、每个库的分表数仍为32张表(0~31)。
首先我们部署Sharding-Scaling+Sharding-Proxy进行在线数据迁移及数据分片处理,具体如下:
1)、部署Sharding-Proxy
该服务的作用是一个数据库中间件,我们在此服务上编辑好分库分表规则后,Sharding-Scaling会把原数据写入Sharding-Proxy,然后由Sharding-Proxy对数据进行路由后写入对应的库和表。
我们可以在Github上下载ShardingSphere对应的版本的源码(演示所用版本为4.1.1)自行编译,也可以下载已经编译好的版本文件。本次演示所用方式为源码编译,其执行程序目录如下:
/shardingsphere-4.1.1/sharding-distribution/sharding-proxy-distribution/target/apache-shardingsphere-4.1.1-sharding-proxy-bin.tar.gz
找到编译执行程序后进行解压!之后编辑