java(多线程)实现高性能数据同步

来源:互联网 发布:js定时弹出遮罩层 编辑:程序博客网 时间:2024/06/01 22:27

需要将生产环境上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);  

 

 

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

 

 

Java代码 
  1. package com.dlbank.domain;    

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

 

原创粉丝点击