如何高效安全的将资源同步到本地数据库
背景
现在软件开发很多是多系统多模块,经常会遇到要将设备、人员、区域等资源同步到本系统数据库保存一份。资源同步常用的方案为全量同步和增量同步结合的方式,全量同步一般情况下为项目启动时和每天定时任务去同步所需资源,增量同步一般利用mq去接收资源的变更(增删改)通知然后修改到本地数据库。这里介绍下如何进行高效安全的全量同步。
全量同步
准备工作:
假设要同步的表为region表,关键字段为(id, name, create_time, update_time),其他如(name, type, code)等根据业务而不同。另外创建一张region_temp表字段同region表。
调用其他系统的分页接口获取全量的region资源数据,并批量插入到region_temp表。
region_temp表中已有最近同步的全量资源数据,但是,我们最终的目的是全量更新region表,针对以下场景分别阐述如何更新region表:
- 新增的数据
insert into region (id, name, code, create_time, update_time)
select t.id, t.name, t.code, t.create_time, t.update_time
from region_temp t
left join tb_region r on a.id = r.id
where r.id is null
该SQL做的是将新增的数据插入到region表中。该SQL将region_temp表与region表左联接,左联接的结果再通过region表的id为空条件过滤记录,region_temp的数据较新,因此region_temp中存在而region中不存在的id必然是需要新增到region中的记录。
- 修改的数据
update tb_region set name = m.name, code=m.code, create_time=m.create_time, update_time=m.update_time from
(select t.id as id ,t.name as name , t.code as code, t.create_time as create_time, t.update_time as update_time
from region_temp t inner join tb_region r on a.id = r.id and r.update_time < t.update_time) m
where region.id = m.t_id
该SQL做的是将修改的数据修改到region表,其中,r.update_time < t.update_time的条件考虑到在做凌晨全量同步的过程中,可能发生了单条记录的更新操作(增量同步已经更新过了),因此,region表的update_time比region_temp表的update_time时间要早的记录才需要做批量的update操作,其余的以当前region表的记录为准。
- 删除的数据
update region set status = -1
from
(select r.id as id
from region r left join region_temp t on t.id = r.id
where t.id is null ) m
where region.id = m.id
该SQL做的是删除region表需要删除的记录。该SQL将region_temp表与region表右联接,右联接的结果再通过region_temp表的id为空条件过滤记录,region_temp中不存在而region中存在的id,便是需要删除的记录。若是同步期间新增的记录,那么,再引入a.create_time <= ${sync_time},即小于等于触发同步操作的时间。
被筛选出来的需要删除的记录可以通过spring的event模型发送内部消息,通知其它模块删除了region表某条记录。
伪代码:
//先清空临时表再批量插入同步过来的数据
baseMapper.deleteTemplateAll();
baseMapper.insertBatch(regionList);
//执行更新
baseMapper.insertNew();
baseMapper.updateChange();
baseMapper.updateDelete();
以上首先将数据保存到本地region_temp表,最后再在数据库层面进行region表的全量更新(不仅减少了和数据库传输的开销,而且更加高效)。若过程中出现获取全量资源失败,也不影响region表的正常crud,同时兼顾到删除的变更通知。另外,http全量获取资源的接口返回值中也不需要带上is_delete的软删除字段便可完成同步。