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库的数据