|
|
51CTO旗下网站
|
|
移动端

干货分享:利用Java多线程技术导入数据到Elasticsearch

作者花了3天的时间,利用java线程池框架Executors中的FixedThreadPool线程池重写了MTE导入工具,单台服务器导入效率提高十几倍(合理调整线程数据,效率更高)。

作者:Wooola来源:头条科技|2019-07-15 16:10

 前言

干货分享:利用java多线程技术导入数据到Elasticsearch

近期接到一个任务,需要改造现有从mysql往Elasticsearch导入数据MTE(mysqlToEs)小工具,由于之前采用单线程导入,千亿数据需要两周左右的时间才能导入完成,导入效率非常低。所以楼主花了3天的时间,利用java线程池框架Executors中的FixedThreadPool线程池重写了MTE导入工具,单台服务器导入效率提高十几倍(合理调整线程数据,效率更高)。

关键技术栈

  • Elasticsearch
  • jdbc
  • ExecutorService\Thread
  • sql

工具说明

maven依赖

  1. <dependency> 
  2.  <groupId>mysql</groupId> 
  3.  <artifactId>mysql-connector-java</artifactId> 
  4.  <version>${mysql.version}</version> 
  5. </dependency> 
  6. <dependency> 
  7.  <groupId>org.elasticsearch</groupId> 
  8.  <artifactId>elasticsearch</artifactId> 
  9.  <version>${elasticsearch.version}</version> 
  10. </dependency> 
  11. <dependency> 
  12.  <groupId>org.elasticsearch.client</groupId> 
  13.  <artifactId>transport</artifactId> 
  14.  <version>${elasticsearch.version}</version> 
  15. </dependency> 
  16. <dependency> 
  17.  <groupId>org.projectlombok</groupId> 
  18.  <artifactId>lombok</artifactId> 
  19.  <version>${lombok.version}</version> 
  20. </dependency> 
  21. <dependency> 
  22.  <groupId>com.alibaba</groupId> 
  23.  <artifactId>fastjson</artifactId> 
  24.  <version>${fastjson.version}</version> 
  25. </dependency> 

java线程池设置

默认线程池大小为21个,可调整。其中POR为处理流程已办数据线程池,ROR为处理流程已阅数据线程池。

  1. private static int THREADS = 21
  2. public static ExecutorService POR = Executors.newFixedThreadPool(THREADS); 
  3. public static ExecutorService ROR = Executors.newFixedThreadPool(THREADS); 

定义已办生产者线程/已阅生产者线程:ZlPendProducer/ZlReadProducer

  1. public class ZlPendProducer implements Runnable { 
  2.  ... 
  3.  @Override 
  4.  public void run() { 
  5.  System.out.println(threadName + "::启动..."); 
  6.  for (int j = 0; j < Const.TBL.TBL_PEND_COUNT; j++) 
  7.  try { 
  8.  .... 
  9.  int size = 1000
  10.  for (int i = 0; i < count; i += size) { 
  11.  if (i + size > count) { 
  12.  //作用为size***没有100条数据则剩余几条newList中就装几条 
  13.  size = count - i; 
  14.  } 
  15.  String sql = "select * from " + tableName + " limit " + i + ", " + size; 
  16.  System.out.println(tableName + "::sql::" + sql); 
  17.  rs = statement.executeQuery(sql); 
  18.  List<HistPendingEntity> lst = new ArrayList<>(); 
  19.  while (rs.next()) { 
  20.  HistPendingEntity p = PendUtils.getHistPendingEntity(rs); 
  21.  lst.add(p); 
  22.  } 
  23.  MteExecutor.POR.submit(new ZlPendConsumer(lst)); 
  24.  Thread.sleep(2000); 
  25.  } 
  26.  .... 
  27.  } catch (Exception e) { 
  28.  e.printStackTrace(); 
  29.  } 
  30.  } 
  31. public class ZlReadProducer implements Runnable { 
  32.  ...已阅生产者处理逻辑同已办生产者 

定义已办消费者线程/已阅生产者线程:ZlPendConsumer/ZlReadConsumer

  1. public class ZlPendConsumer implements Runnable { 
  2.  private String threadName; 
  3.  private List<HistPendingEntity> lst; 
  4.  public ZlPendConsumer(List<HistPendingEntity> lst) { 
  5.  this.lst = lst; 
  6.  } 
  7.  @Override 
  8.  public void run() { 
  9.  ... 
  10.  lst.forEach(v -> { 
  11.  try { 
  12.  String json = new Gson().toJson(v); 
  13.  EsClient.addDataInJSON(json, Const.ES.HistPendDB_Index, Const.ES.HistPendDB_type, v.getPendingId(), null); 
  14.  Const.COUNTER.LD_P.incrementAndGet(); 
  15.  } catch (Exception e) { 
  16.  e.printStackTrace(); 
  17.  System.out.println("err::PendingId::" + v.getPendingId()); 
  18.  } 
  19.  }); 
  20.  ... 
  21.  } 
  22. public class ZlReadConsumer implements Runnable { 
  23.  //已阅消费者处理逻辑同已办消费者 

定义导入Elasticsearch数据监控线程:Monitor

监控线程-Monitor为了计算每分钟导入Elasticsearch的数据总条数,利用监控线程,可以调整线程池的线程数的大小,以便利用多线程更快速的导入数据。

  1. public void monitorToES() { 
  2.  new Thread(() -> { 
  3.  while (true) { 
  4.  StringBuilder sb = new StringBuilder(); 
  5.  sb.append("已办表数::").append(Const.TBL.TBL_PEND_COUNT) 
  6.  .append("::已办总数::").append(Const.COUNTER.LD_P_TOTAL) 
  7.  .append("::已办入库总数::").append(Const.COUNTER.LD_P); 
  8.  sb.append("~~~~已阅表数::").append(Const.TBL.TBL_READ_COUNT); 
  9.  sb.append("::已阅总数::").append(Const.COUNTER.LD_R_TOTAL) 
  10.  .append("::已阅入库总数::").append(Const.COUNTER.LD_R); 
  11.  if (ldPrevPendCount == 0 && ldPrevReadCount == 0) { 
  12.  ldPrevPendCount = Const.COUNTER.LD_P.get(); 
  13.  ldPrevReadCount = Const.COUNTER.LD_R.get(); 
  14.  start = System.currentTimeMillis(); 
  15.  } else { 
  16.  long end = System.currentTimeMillis(); 
  17.  if ((end - start) / 1000 >= 60) { 
  18.  start = end; 
  19.  sb.append("\n#########################################\n"); 
  20.  sb.append("已办每分钟TPS::" + (Const.COUNTER.LD_P.get() - ldPrevPendCount) + "条"); 
  21.  sb.append("::已阅每分钟TPS::" + (Const.COUNTER.LD_R.get() - ldPrevReadCount) + "条"); 
  22.  ldPrevPendCount = Const.COUNTER.LD_P.get(); 
  23.  ldPrevReadCount = Const.COUNTER.LD_R.get(); 
  24.  } 
  25.  } 
  26.  System.out.println(sb.toString()); 
  27.  try { 
  28.  Thread.sleep(3000); 
  29.  } catch (InterruptedException e) { 
  30.  e.printStackTrace(); 
  31.  } 
  32.  } 
  33.  }).start(); 

初始化Elasticsearch:EsClient

  1. String cName = meta.get("cName");//es集群名字 
  2. String esNodes = meta.get("esNodes");//es集群ip节点 
  3. Settings esSetting = Settings.builder() 
  4.  .put("cluster.name", cName) 
  5.  .put("client.transport.sniff"true)//增加嗅探机制,找到ES集群 
  6.  .put("thread_pool.search.size"5)//增加线程池个数,暂时设为5 
  7.  .build(); 
  8. String[] nodes = esNodes.split(","); 
  9. client = new PreBuiltTransportClient(esSetting); 
  10. for (String node : nodes) { 
  11.  if (node.length() > 0) { 
  12.  String[] hostPort = node.split(":"); 
  13.  client.addTransportAddress(new TransportAddress(InetAddress.getByName(hostPort[0]), Integer.parseInt(hostPort[1]))); 
  14.  } 

初始化数据库连接

  1. conn = DriverManager.getConnection(url, user, password); 

启动参数

  1. nohup java -jar mte.jar ES-Cluster2019 node1:9300,node2:9300,node3:9300 root 123456! jdbc:mysql://ip:3306/mte 130 130 >> ./mte.log 2>&1 & 

参数说明

ES-Cluster2019 为Elasticsearch集群名字

node1:9300,node2:9300,node3:9300为es的节点IP

130 130为已办已阅分表的数据

程序入口:MteMain

干货分享:利用java多线程技术导入数据到Elasticsearch
 
 
  1. // 监控线程 
  2. Monitor monitorService = new Monitor(); 
  3. monitorService.monitorToES(); 
  4. // 已办生产者线程 
  5. Thread pendProducerThread = new Thread(new ZlPendProducer(conn, "ZlPendProducer")); 
  6. pendProducerThread.start(); 
  7. // 已阅生产者线程 
  8. Thread readProducerThread = new Thread(new ZlReadProducer(conn, "ZlReadProducer")); 
  9. readProducerThread.start(); 

【编辑推荐】

  1. 在阿里一年,我颠覆了曾坚信不疑的技术思维
  2. 摩纳哥成***5G全覆盖国家,由华为技术支持
  3. 50岁大叔回剑桥读AI博士,研发机器人用AI收生菜
  4. 微服务架构之「 容器技术 」
  5. Intel公布三大全新封装技术:未来CPU就长这样
【责任编辑:张燕妮 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

这就是5G

这就是5G

5G那些事儿
共15章 | armmay

99人订阅学习

16招轻松掌握PPT技巧

16招轻松掌握PPT技巧

GET职场加薪技能
共16章 | 晒书包

363人订阅学习

20个局域网建设改造案例

20个局域网建设改造案例

网络搭建技巧
共20章 | 捷哥CCIE

746人订阅学习

读 书 +更多

网管员必读——网络组建

本书以一个模拟局域网组建为思路,介绍了与局域网组建各主要方面相关的知识及组建、配置方法。本书所介绍的内容主要包括:局域网组建规划、...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO官微