java实现高性能的数据同步

转载: http://www.javaeye.com/topic/824519

 

最近在做一个银行的生产数据脱敏系统,今天写代码时遇到了一个“瓶颈”,脱敏系统需要将生产环境上Infoxmix里的数据原封不动的Copy到另一台 Oracle数据库服务器上,然后对Copy后的数据作些漂白处理。为了将人为干预的因素降到最低,在系统设计时采用Java代码对数据作Copy,思路 如图:



    首 先在代码与生产库间建立一个Connection,将读取到的数据放在ResultSet对象,然后再与开发库建立一个Connection。从 ResultSet取出数据后通过TestConnection插入到开发库,以此来实现Copy。代码写完后运行程序,速度太慢了,一秒钟只能Copy 一千条数据,生产库上有上亿条数据,按照这个速度同步完要到猴年马月呀,用PreparedStatement批处理速度也没有提交多少。我想能不能用多 线程处理,多个人干活总比一个人干活速度要快。
    假设生产库有1万条数据,我开5个线程,每个线程分2000条数据,同时向开发库里插数据,Oracle支持高并发这样的话速度至少会提高好多倍,按照这 个思路重新进行了编码,批处理设置为1万条一提交,统计插入数量的变量使用 java.util.concurrent.atomic.AtomicLong,程序一运行,传输速度飞快CPU利用率在70%~90%,现在一秒钟可 以拷贝50万条记录,没过几分钟上亿条数据一条不落地全部Copy到目标库。

在查询的时候我用了如下语句

Java代码
  1. String queryStr =  "SELECT * FROM xx" ;  
  2. ResultSet coreRs = PreparedStatement.executeQuery(queryStr);  
String queryStr = "SELECT * FROM xx";
ResultSet coreRs = PreparedStatement.executeQuery(queryStr);


实习生问如果xx表里有上千万条记录,你全部查询出来放到ResultSet, 那内存不溢出了么?Java在设计的时候已经考虑到这个问题了,并没有查询出所有的数据,而是只查询了一部分数据放到ResultSet,数据“用完”它 会自动查询下一批数据,你可以用setFetchSize(int rows)方法设置一个建议值给ResultSet,告诉它每次从数据库Fetch多少条数据。但我不赞成,因为JDBC驱动会根据实际情况自动调整 Fetch的数量。另外性能也与网线的带宽有直接的关系。
相关代码

Java代码
  1. package  com.dlbank.domain;    
  2.     
  3. import  java.sql.Connection;    
  4. import  java.sql.PreparedStatement;    
  5. import  java.sql.ResultSet;    
  6. import  java.sql.Statement;    
  7. import  java.util.List;    
  8. import  java.util.concurrent.atomic.AtomicLong;    
  9.     
  10. import  org.apache.log4j.Logger;    
  11.     
  12. /**   
  13.  *<p>title: 数据同步类 </p>     
  14.  *<p>Description: 该类用于将生产核心库数据同步到开发库</p>     
  15.  *@author Tank Zhang    
  16.  */     
  17. public   class  CoreDataSyncImpl  implements  CoreDataSync {    
  18.         
  19.     private  List<String> coreTBNames;  //要同步的核心库表名     
  20.     private  ConnectionFactory connectionFactory;    
  21.     private  Logger log = Logger.getLogger(getClass());    
  22.         
  23.     private  AtomicLong currentSynCount =  new  AtomicLong(0L);  //当前已同步的条数     
  24.         
  25.     private   int  syncThreadNum;   //同步的线程数     
  26.     
  27.     @Override     
  28.     public   void  syncData( int  businessType)  throws  Exception {    
  29.             
  30.         for  (String tmpTBName : coreTBNames) {    
  31.             log.info("开始同步核心库"  + tmpTBName +  "表数据" );    
  32.             // 获得核心库连接     
  33.             Connection coreConnection = connectionFactory.getDMSConnection(4 );    
  34.             Statement coreStmt = coreConnection.createStatement();    
  35.             //为每个线程分配结果集     
  36.             ResultSet coreRs = coreStmt.executeQuery("SELECT count(*) FROM " +tmpTBName);    
  37.             coreRs.next();    
  38.             //总共处理的数量     
  39.             long  totalNum = coreRs.getLong( 1 );    
  40.             //每个线程处理的数量     
  41.           //  long  ownerRecordNum =( long ) Math.ceil((totalNum / syncThreadNum));    
  42.             long ownerRecordNum = ((totalNum % syncThreadNum) == 0) ? (totalNum / syncThreadNum):((totalNum / syncThreadNum) + 1);
  43.             log.info("共需要同步的数据量:" +totalNum);    
  44.             log.info("同步线程数量:" +syncThreadNum);    
  45.             log.info("每个线程可处理的数量:" +ownerRecordNum);    
  46.             // 开启五个线程向目标库同步数据     
  47.             for ( int  i= 0 ; i < syncThreadNum; i ++){    
  48.                 StringBuilder sqlBuilder = new  StringBuilder();    
  49.                 //拼装后SQL示例     
  50.                 //Select * From dms_core_ds Where id between 1 And 657398     
  51.                 //Select * From dms_core_ds Where id between 657399 And 1314796     
  52.                 //Select * From dms_core_ds Where id between 1314797 And 1972194     
  53.                 //Select * From dms_core_ds Where id between 1972195 And 2629592     
  54.                 //Select * From dms_core_ds Where id between 2629593 And 3286990     
  55.                 //..     
  56.                 sqlBuilder.append("Select * From " ).append(tmpTBName)    
  57.                         .append(" Where id between "  ).append(i * ownerRecordNum + 1 )    
  58.                         .append( " And " )    
  59.                         .append((i * ownerRecordNum + ownerRecordNum));    
  60.                 Thread workThread = new  Thread(    
  61.                         new  WorkerHandler(sqlBuilder.toString(),businessType,tmpTBName));    
  62.                 workThread.setName("SyncThread-" +i);    
  63.                 workThread.start();    
  64.             }    
  65.             while  (currentSynCount.get() < totalNum);    
  66.             //休眠一会儿让数据库有机会commit剩余的批处理(只针对JUnit单元测试,因为单元测试完成后会关闭虚拟器,使线程里的代码没有机会作提交操作);     
  67.             //Thread.sleep(1000 * 3);     
  68.             log.info( "核心库" +tmpTBName+ "表数据同步完成,共同步了"  + currentSynCount.get() +  "条数据" );    
  69.         }    
  70.     }// end for loop     
  71.         
  72.     public   void  setCoreTBNames(List<String> coreTBNames) {    
  73.         this .coreTBNames = coreTBNames;    
  74.     }    
  75.     
  76.     public   void  setConnectionFactory(ConnectionFactory connectionFactory) {    
  77.         this .connectionFactory = connectionFactory;    
  78.     }    
  79.         
  80.     public   void  setSyncThreadNum( int  syncThreadNum) {    
  81.         this .syncThreadNum = syncThreadNum;    
  82.     }    
  83.         
  84.     //数据同步线程     
  85.     final   class  WorkerHandler  implements  Runnable {    
  86.         ResultSet coreRs;    
  87.         String queryStr;    
  88.         int  businessType;    
  89.         String targetTBName;    
  90.         public  WorkerHandler(String queryStr, int  businessType,String targetTBName) {    
  91.             this .queryStr = queryStr;    
  92.             this .businessType = businessType;    
  93.             this .targetTBName = targetTBName;    
  94.         }    
  95.         @Override     
  96.         public   void  run() {    
  97.             try  {    
  98.                 //开始同步     
  99.                 launchSyncData();    
  100.             } catch (Exception e){    
  101.                 log.error(e);    
  102.                 e.printStackTrace();    
  103.             }    
  104.         }    
  105.         //同步数据方法     
  106.         void  launchSyncData()  throws  Exception{    
  107.             // 获得核心库连接     
  108.             Connection coreConnection = connectionFactory.getDMSConnection(4 );    
  109.             Statement coreStmt = coreConnection.createStatement();    
  110.             // 获得目标库连接     
  111.             Connection targetConn = connectionFactory.getDMSConnection(businessType);    
  112.             targetConn.setAutoCommit(false ); // 设置手动提交     
  113.             PreparedStatement targetPstmt = targetConn.prepareStatement("INSERT INTO "  + targetTBName+ " VALUES (?,?,?,?,?)" );    
  114.             ResultSet coreRs = coreStmt.executeQuery(queryStr);    
  115.             log.info(Thread.currentThread().getName()+"'s Query SQL::" +queryStr);    
  116.             int  batchCounter =  0//累加的批处理数量     
  117.             while  (coreRs.next()) {    
  118.                 targetPstmt.setString(1 , coreRs.getString( 2 ));    
  119.                 targetPstmt.setString(2 , coreRs.getString( 3 ));    
  120.                 targetPstmt.setString(3 , coreRs.getString( 4 ));    
  121.                 targetPstmt.setString(4 , coreRs.getString( 5 ));    
  122.                 targetPstmt.setString(5 , coreRs.getString( 6 ));    
  123.                 targetPstmt.addBatch();    
  124.                 batchCounter++;    
  125.                 currentSynCount.incrementAndGet();//递增     
  126.                 if  (batchCounter %  10000  ==  0 ) {  //1万条数据一提交     
  127.                     targetPstmt.executeBatch();    
  128.                     targetPstmt.clearBatch();    
  129.                     targetConn.commit();    
  130.                 }    
  131.             }    
  132.             //提交剩余的批处理     
  133.             targetPstmt.executeBatch();    
  134.             targetPstmt.clearBatch();    
  135.             targetConn.commit();    
  136.             //释放连接      
  137.             connectionFactory.release(targetConn, targetPstmt,coreRs);    
  138.         }    
  139.     }    
  140. }   
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值