点击↑上方↑蓝色“编了个程”关注我~
这是Yasin的第 51 篇原创文章
Y说
最近忙得飞起,有好长一段时间没写文章了。
按照惯例还是来一张图,图为某天活动日,下班比较早,在小区附近公园散步所拍。
业余时间在和几个小伙伴一起做一个很有意思的小程序,估计国庆后可以发布,到时候欢迎大家关注~
前段时间在工作中做了数据模型的变更,前前后后也是花了一个多月,所以做个数据源切换的一般性的总结,供参考。
什么是数据源切换
本文想总结的是“数据源切换”的一般步骤。这里说的数据源切换,指的是「数据库层面的变更」。比如:从老的表设计迁移到新的表设计,从单表切换到分库分表,把某些表从一个库迁移到另一个库里面,甚至从mysql迁移到mongodb, 搜索引擎等数据源。
这种情况是经常会遇到的。比如以前的表模型设计得不合理、业务域划分得不合理、数据量增长太快需要分库分表等场景。
但程序又是在线上一直运行的,所以如何在数据迁移的过程中,保证数据的完整性和正确性,降低对用户的影响,是我们迁移要考虑的问题。
存量数据和增量数据
所谓存量数据,指的就是到目前为止已经存在的数据。而增量数据指的是我们在迁移过程中以及迁移后,所产生的新的数据。
在迁移的时候,要同时考虑存量数据和增量数据的迁移,确保切换到新的数据源时,数据是准确且完善的。
迁移步骤
具体的迁移步骤大概分为下面几步:
1 增量数据迁移
应该先迁移增量,再迁移全量,否则有可能会漏掉一些数据。增量数据迁移比较通用的方案是「双写」——即同时把数据写入到老数据源和新数据源。具体实施下来根据不同的场景,有两种方案。
一种方案是改代码,在代码中双写,在配置上添加一个开关。典型的伪代码大概长这样:
if switch == 'old':
writeToOld(data)
elif switch == 'new':
writeToNew(data)
elif switch == 'both':
writeToOld(data)
try:
writeToNew(data)
catch e:
log(e)
这里有两个细节,第一个是可以在改造的时候顺便把切换到新数据源的开关和逻辑写了,这样以后只需要切换开关就可以了,不用重复开发代码发布;第二个是在both的时候,可以用try catch打日志的方式,把新数据源的异常吃掉,这样即使发生了异常也不影响业务。这里发生异常是很常见的,比如要更新的数据新库还没有。
另一种增量数据迁移的方式适用于你的数据模型不需要变更的情况,只是挪一下位置。这时候可以使用监听binlog的方式来做。有些公司有对应的中间件来做这个(比如阿里的精卫),基本原理是监听源数据的binlog的变化,然后发送消息,然后业务方自己写处理消息的简单逻辑,写入到目标数据源。但这种方式只适合数据模型不变或者变更比较简单的情况。
2 存量数据迁移
增量数据迁移的逻辑写好后,就可以做存量数据迁移了。存量数据迁移也分两种。一种是自己写代码或者脚本,循环从老的数据源查出数据,迁移到新的数据源。另一种是借用数据仓储,从老的数据源导入到数仓离线数据表,然后再导入到新的数据源。
但无论使用哪种方式,存量数据的迁移都是有一定风险的,尤其是在迁移过程中,如果发生了更新操作,可能新的数据源就感知不到,所以要尽量缩短迁移时间,在业务使用低频的时间段来做这个操作。
这里如果是数据量小,建议写脚本,一条条迁移能降低增量更新引起的脏数据风险。如果数据量大,建议用数仓,会方便一些。
3 双读和数据比对
数据迁移完成后,下一步要做的就是数据比对。这里比对要分两个层面去比对,一个是数据模型层面,一个是代码模型层面。
数据模型层面肯定是要全量比对的。首先要比对总的数量,然后要比对每一条数据是否和源数据一致。这里可以选择抽样的方式去比对,但更保险的还是写代码或者脚本全量比对,如果发现有不对的数据就即使告警,修复数据和双写代码。这里需要注意的是在迁移的过程中一定要保留一个源数据的唯一标识符,比如id或code之类的,这样才能将新老数据一一对应。
如果你的数据迁移涉及到数据模型的变更。比如字段的修改、重命名、表模型的修改等。那就要改代码,进行双读。双读的伪代码如下:
if switch == 'old':
return readOld()
elif switch == 'new':
return readNew()
elif switch == 'both':
oldData = readOld()
try:
newData = readNew()
diffAndLog(oldData, newData)
catch e:
log(e)
return oldData
双读是要在代码中再比较一轮的。因为如果涉及到数据模型的变更,即使你能保证数据库层面是正确的,也不一定能保证代码层面也跟原来是一模一样的。有可能会漏字段、sql写得不对等等。通过在代码中diff,可以发现此类问题。
4 切读
上述步骤都做完后,观察一段时间(一般一两周就行)。确定没问题后,就可以开始切读了,把读开关切换到new,继续观察几天。
5 切写
然后就是切写了,把写开关切换到new,继续观察几天
6 清理旧代码、老数据
最后一步就是清理老的代码和数据了。安全起见,建议在迁移后较长一段时间后去做(尤其是数据,最好半年后再删)。
总结
以上就是整个数据源切换的一般步骤,适合绝大多数数据迁移场景。但在实操过程中,还是要尽量小心,多线上观察,做好日志和告警,毕竟动数据不是小事。
关于作者
我是Yasin,一个爱写博客的技术人
微信公众号:编了个程(blgcheng)
个人网站:https://yasinshaw.com
欢迎关注这个公众号
盘点Java中的那些常用的Garbage Collector