需求背景:有一个业务方需要定期跟数据库进行数据同步,就是需要定期往数据库中同步部分数据,而这些数据并不能被当前系统直接使用,需要做一些处理同步到系统所使用的数据库中,处理比较复杂,没办法进行实时的同步,所以需要写个定时任务,将处理后的数据进行同步,说白了就是数据库中两个表的数据同步。

实现这个需求,首先想到的是直接通过sql 进行同步,表之间数据同步无非是三种操作:更新,删除,插入,假设两个表 dst,srcdst中有id,name,auth三个字段,src中有id,name,dsc三个字段,需要将src 中的id,name同步到dst中去,如图所示:

dstsrc中都存在的数据,只需要按照src中的数据,批量更新dst中的数据即可,sql语句可能是这样:

update dst, src set dst.id=src.id,dst.name=src.name where src.id=dst.id;

更新完成之后,需要向dst表中插入在src存在,而dst中不存在的数据,简单的sql可能是这样:

insert dst (id,name,auth) select src.id,src.name,'1' from src where not exists(select dst.id from dst where src.id=dst.id);

再接着是删除,将dst中存在src中不存在的数据称从dst中删除,sql如下:

delete from dst where not exists(select src.id from src where src.id=dst.id );

注意:如果是用的mysql上述同步删除语句中的dst表明不能简写

有了这三条语句,写个定时任务,依次执行即可,如果是用的spring,通常会用@Scheduled具体使用可以google一下,非常方便。如果需要事务可以使用@Transactional,这些是spring通过aop集成好的,可以声明式使用,但是提供的粒度不够灵活,使用也会有些限制,如果想更灵活点话,可以使用编程式事务。而如果数据库开启了autocommit功能,其本身就会有事务,不需要逻辑代码中再加事务(不是绝对的,当然要看自己需要,要记住autocommit只保证每一条sql是一个事务)。如果要看看自己的数据库是否开启了autocommit,可以用下面的sql

show variables like 'autocommit'

这种方式的特点就是思路很简单。不需要写太多的java代码(定时任务都可以直接用spring封装好的注解,只需要写个类,写个函数,如果使用orm的话,然后实现mybatishibernate相关的daoservice)。而这种实现的问题也很明显,就是你对整个同步过程可控的东西很有限,最多通过事务保证如果同步失败了,整体回滚。而且当同步逻辑比较复杂的时候,比如说表中字段比较多,而且同步部分字段,同步的字段需要join其他表才能决定需不需要同步,这些逻辑全部写在sql中会导致sql很臃肿,而且更容易出错,更严重的是出错了你却什么都做不了,也不知道具体哪里同步出错了。肿么办?那就一条一条的来呗。


要想对同步过程拥有足够的控制,就只能将需要同步的数据全部load到内存,然后通过写程序进行遍历。具体过程应该是这样的:
 1. 将src中的数据全部load到内存中,如果数据量比较大(通常都是这样,内存一次性放不下),就进行分页loadsql语句如下:

select * from src limit 'pageSize' offset 'offset'  

其中'pageSize'通过程序设定,而'offset'就是pageSize*pageNo,比如说每页100条数据,取第一页的数据就是select * from src limit 100 offset 100*1

对取出来的数据进行循环遍历,java程序简写如下:

private void syncData(){Date curTime = new Date();List<MyData> datas = myDataDao.getDatas(pageNo,pageSize);//访问的是src表for(MyData data : datas){if(needToUpdate(data))//src表中的数据在dst中已经存在就update,否则就insertupdate(data);else insert(data);}deleteDatasFromNow(curTime); //删除当前同步时间之前的数据
}

那么needToUpdate做的就是判断一下data是否在dst中存在,这里需要一个唯一标识来确定当前data,通常是一个字段或几个联合确定唯一的data。所以needToUpdate可能如下:

private boolean needToUpdate(MyData data){Optional<MyData> myData = myDataDao.findByName(data.getName);//这里访问的是dst表if(myData.isPresent()){return true;}else return false;
}

updateinsert都会改变dst中的updateTime,所以在删除的时候就可以通过updateTime是否晚于curTime来判断当前数据是否更新过或新插入的,如果不是,那就是需要删除的数据,所以deleteDatasFromNow()如下:

private void deleteDatasFromNow(Date curTime){List<MyData> datasNeedToDel = myDataDao.getDatasNeedToDel(curTime);for(MyData data : datasNeedToDel){delete(data);}
}

这样整个同步过程就完成了,如果想要打印同步日志或将同步过程记录下来,就可以在update(),insert(),delete()中插入日志操作就行了,就拿update()来说的话,可能像下面的情况:

private void update(MyData data){try{myDataDao.update(data);}catch(Exception e){logger.error("数据"+data.getName()+"同步出错");//这里是打印日志,如果需要也可以保存到数据库return;}logger.info("数据"+data.getName()+"同步成功");
}

通过日志文件分析(如果将操作保存到数据库的话也可以直接查询数据库),可以清晰的知道哪些数据进行了更新,哪些数据是新插入的,哪些是删除了的,然后还可以进行统计,共更新了多少数据,多少成功了,多少失败了。业务层面就可以了解更多有关数据同步的信息。而且这种操作使sql非常简单,也不太容易出错。但是一个很明显的问题也暴露出来了,那就是效率问题,如果通过这种方式,势必要遍历每一条数据,对需要update,insert,delete的数据需要一个一个地进行访问数据库,而且对于needToUpdate(MyData data)中也额外访问了一次数据库,亲测这个效率真是低惊人。而且这种实现中会导致,即便什么数据都不更新,也会完全遍历一遍数据,访问同样多的数据库,所需的时间还是那么久,这是在业务层面无法容忍的。于是就有了思路三。


既然上述方法的主要问题就是访问数据库过多导致效率底下,那么就必须尽可能减少数据库的访问和遍历的次数。那就需要紧贴业务需求,针对具体的需求进行改善。我遇到的需求就是每次数据同步的过程中,大部分数据都是不变的,只有少部分新增和删除,针对这个需求进行了下面的优化:
  * 不再每次将所有的src中的数据取出来,而是将需要删除的和需要更新的,以及需要新插入的分别取出来,这样数据取来之后就可以直接进行update,insert,delete了,不需要再进行额外的比较判断的了
  * 大部分数据不变,因此进行update的时候不再单条进行更新,直接进行分页批量更新,比如说每次更新100条或者更多,以提高更新的效率。
  通过这些优化大副提高了同步的效率,其中需要注意的是当写稍微复杂点的sql的时候一定要注意,虽然都能得到相同的查询结果,可能效率相差十万八千里,在实现这个数据同步是,就因为join位置放的不对,导致查询需要插入的数据时灰常慢,所以好好学学sql优化还是很有必要的,网上有很多大牛介绍,可以多看看,或者用的时候再学(我通常是这样。。。)

数据库数据定期同步实现相关推荐

  1. sqlserver2008基于发布/订阅功能实现主从数据库数据实时同步

    网上关于sqlserver基于发布/订阅实现数据同步的文章很多,大多介绍不详细,各种copy.为实现发布服务器.订阅服务器数据库实时同步,近期花了几天时间认真研究了一下,并实践验证通过,希望本文能帮助 ...

  2. 达梦DMHS异构数据库数据实时同步软件速知

    DMHS简介 DMHS(Heterogeneous database Synchronization for DM)是达梦数据库公司推出的一款异构数据库数据实时同步工具软件.同步源端支持ORACLE系 ...

  3. EF框架中,在实体中手动更新字段,数据库数据未同步到程序中应该怎么解决呢?

    在一些技术不是很强的选手手中,设计数据库时,难免会未考虑到某些字段,只能到后期实现功能时,才能觉察出来数据库中或是少写字段,或是多加了无用的字段,故我们还不得不去数据库中做些手脚. 本文列举的是在as ...

  4. oracle数据库实现不同数据库之间的表格数据定期同步

    1.创建数据库连接 CREATE PUBLIC DATABASE LINK DBLINK01 CONNECT TO 用户名 IDENTIFIED BY 密码 USING '(DESCRIPTION = ...

  5. 基于数据库数据增量同步_基于canal实现分布式数据同步

    应用场景 分布式架构中,数据同步常常是个大问题.例如,mysql中的数据,可能在ElasticSearch有一份索引,在redis有一份缓存,在Nginx有一份缓存,这时候只要你修改了mysql中的数 ...

  6. 基于数据库数据增量同步_基于 Flink SQL CDC 的实时数据同步方案

    简介:Flink 1.11 引入了 Flink SQL CDC,CDC 能给我们数据和业务间能带来什么变化?本文由 Apache Flink PMC,阿里巴巴技术专家伍翀 (云邪)分享,内容将从传统的 ...

  7. linux服务器数据同步,Linux服务器数据定期同步和备份方式

    数据安全是做数据分析的人需要关注的一大问题.对于我们分析的关键数据.使用的关键脚本都需要定期备份. scp 最简单的备份方式,就是使用cp (本地硬盘)或scp (远程硬盘)命令,给自己的结果文件新建 ...

  8. 基于canal根据配置实现数据库数据的同步

    可以通过配置对源数据库的指定表同步指定字段,无需修改代码 具体配置如下,可通过字段映射关系,自动生成对应sql执行, canal:server: 172.16.4.62:11111# canal in ...

  9. SQL2005 数据库数据同步

    由于公司业务原因,一台服务器放置于网通机房,一台服务器放置于电信机房,不同业务需要共享一台数据库,原先的做法是把数据库放置于网通机房服务器上,电信服务器远程连接网通机房服务器上数据库,由于网通机房带宽 ...

最新文章

  1. hql中常用函數介紹二
  2. Linux 技术篇-文件大小查看方法实例演示,查看指定文件大小,查看列表下所有文件夹和文件的大小
  3. 北卡教堂山计算机科学专业,UNC的CS「北卡罗来纳大学教堂山分校计算机科学系」...
  4. go程序的跨平台编译
  5. 【OpenCV 例程200篇】19. 图像的圆形遮罩
  6. HDFS文件系统存储机制
  7. springcloud maven打包部署
  8. Java网络编程——Socket
  9. Linux服务器文件同步(NFS服务)
  10. android avrcp处理流程,(VR虚拟现实)Android 蓝牙AVRCP功能的实现.doc
  11. Python项目实战:使用selenium爬取拉勾网数据
  12. Peta数据集识别性别
  13. c语言 整数拆分,C++ 整数拆分方法详解
  14. 带有CAN模块通讯的labview转化为应用程序(exe)CAN模块启动失败问题解决办法
  15. 大屏监控 Metabase 集成到 Java 项目
  16. 漫画分销系统服务器配置,漫画分销系统
  17. go time包定时器和断续器
  18. ueditor 配置window.UEDITOR_HOME_URL路径不起作用,提示引用不到该路径,引用的确是另一个项目路径
  19. 数学分析(5): 导数
  20. Android拦截电话

热门文章

  1. RTX3070、3070Ti相当于什么水平?
  2. pycharm this license has been suspended
  3. 理财笔记 - 生日前的幻想
  4. android版iphone6s,iPhone6S的性能现在相当于什么档次的安卓机?答案使很多品牌汗颜...
  5. hashcode是什么?有什么作用?
  6. Jmeter书中不会教你的(39)——快递时效查询9在beanshell中写java方法
  7. 声音衰减计算软件_软件衰减的最重要因素
  8. python为什么输出none_python为什么不显示none
  9. Java服务CPU高如何定位解决
  10. 两个玻璃球 测试极限高度