有A&B两个数据库,以A为主,B要同步A上的数据,同步涉及数据的增删改查,且要每天同步一次。条件限制是,只能调用中台提供的接口操作数据库,不能写SQL。
由于只能通过中台调用接口操作数据库,所以解决方法被限制在后端代码层面。至于每天执行一次,使用SpringBoot的定时任务注解@Scheduled就可以了。
最好的解决思路写在最后。
对于增加和修改,将A库中所有的数据通过接口拿到一个List里面,遍历这个List,每一趟检查B库中是否存在主键对应的数据,如果存在,则将数据更新,如果不存在,则直接插入。
对于A库中删除的数据,需要反向遍历,即将B库的数据拿出来,将数据进行遍历对比,如果发现B库中有的数据而A库中没有,则删除B库中的数据。
可以看到这个方案需要不停的遍历,效率很低。
如题,通过中台拿到A中的数据,然后把B库清空,再写A库的数据。
逻辑十分简单,但清空数据库很吓人也会导致很多问题,非常不优雅。
大致思路是通过数据中台拿到A库的List和B库的List,比对这两个List的差别,只把差别写到数据库。
将A库中的数据拿到newList中,B库中的数据拿到oldList中,对newList进行遍历,每次遍历都寻找oldList中是否存在与newList对应的数据(通过主键作为对应标识);如果存在,则判断两个对象是否相等(要重写equals()和hashcode()方法),相等则直接从两个队列中剔除这个数据,不相等则调用接口进行数据更新,更新完了之后将更新过的数据从两个List中剔除;这样遍历完了之后,newList中剩下的没被剔除的数据就是B库需要新增的数据,oldList中剩下的没被剔除的数据就是B库需要删除的数据,需要修改的数据在遍历过程中已完成修改。
由于比对操作是在内存中完成的,所以这个方案的性能比前两个要高不少,十分优雅。
这个方案的伪码如下:
//A库中的数据同步到B库伪码
//A库中的数据拿到newList中,B库中的数据拿到oldList
List<Data> newDataList;
List<Data> oldDataList;
//从数据库中取数据......
//开始遍历比对
for (Data newData : newDataList) {
for (Data oldData : oldDataList) {
//通过主键判断oldData与newData是否为同一条记录(对应)
if (newData.getId().equals(oldData.getId())) {
//主键对应上了,下面判断数据是否发生更改
if (!newData.equals(oldData)) {
//新旧数据不同,发生了更改,将更改落盘到数据库
dataService.updateData(newData);
} else {
//新旧数据一致,说明没有发生更改,什么也不做
}
//新旧数据比对并操作后,将这两个数据从List中“移除”
newData = null;
oldData = null;
}
//两个数据主键没有对应上,说明不为同一条记录,继续遍历
}
//遍历完成之后,如果新数据在旧数据中有主键对应,则肯定会被置null
//没被置null说明这条新数据是新增的,需要调用接口落盘
if (newData != null) {
dataService.insertData(newData);
}
}
//遍历完新数据后,oldDataList中剩下的没被置null的都是需要删除的
for (Data oldData : oldDataList) {
if (oldData != null) {
dataService.deleteDataById(oldData.getId());
}
}
//此时新旧数据库的内容就同步成一样的了