使用生产者和消费者算法加速数据处理

应用场景

数据库里有1120万条数据需要一个不漏的全部处理,然后将处理后的数据再次保存在数据库中。如果数据量小的话,取出来后挨个处理挨个进行保存即可,此时我们面对的是千万级别的大批量的数据,挨个处理挨个保存这种顺序方法非常耗时,不可行。
在这里插入图片描述

方法使用

此时可以选择一个生产者和消费者思路进行处理,如下图顺序方法和生产者消费者方法的对比。
顺序执行的耗时模型处理数据处理数据完成后才可以执行保存数据,耗时是叠加;生产消费方法执行耗时是生产数据消费数据方法并行的执行状态,消耗时间取决于哪个最后执行完成(此图中生产数据执行时间最长)。
在这里插入图片描述

首先创建线程安全的队列,用于生产者和消费者的数据传递。生产者在处理完成一条数据后,把数据放入队列中,消费者不断的检查队列中是否存在数据,如果存在数据就进行取出处理后保存。

private Queue<RelateCountMatrixEntity> queue = new ConcurrentLinkedQueue<>();

这种方式解放生产者 存储数据库这种耗时的瓶颈,把精力放在处理数据的身上;消费者没有了处理数据中繁琐的计算瓶颈,把精力全部放在存储数据上。

生产者代码

public void produce() {
    
    
   //为消费者开启一个线程单独运行
   new Thread(this::consumerQueue).start();
   long startTime = System.currentTimeMillis();

   //处理数据
   Data data = null;
   while ((data = getData()) != null) {
    
    
   	   //处理data数据并将data添加到队列中
   	   queue.offer(data);
       lastId = userAndUserMatrix(lastId);
   }
   //存放到队列中最后一条数据,最后一条记录设置为-1,便于消费者处理
   queue.offer(-1);

   long wasteTime = System.currentTimeMillis() - startTime;
   logger.info("消耗时间:{}", wasteTime);
}

在生产者方法中开启了一个线程运行消费者方法,消费者实时观察队列中是否有存在数据,不存在进行等待一秒避免无意义的CPU空转。消费者会实时判断是否最后一条记录,最后一条记录即退出这个死循环,运行该方法的线程会自动结束。
注意:在写到有死循环的代码时候,要确保有停住这个死循环的机制,不能让它一直无限制的空转.

//消费队列数据
private void consumerQueue() {
    
    
  while (true) {
    
    
      Data data = queue.poll();
      if (data == null) {
    
    
          try {
    
    
              logger.info("队列数据为空,等待1000ms...");
              Thread.sleep(1000);
          } catch (Exception e) {
    
    
              //
          }
      }
      //判断队列中是否是最后一条记录
      if (data != null && data == -1) {
    
    
          break;
      }
      if (data != null) {
    
    
          dao.saveRelateCount(data);
      }
  }
}

关于从库中获取全部记录,在之前的博客中有写到,谢谢查看 使用mongoTemplate id增量快速遍历整张表的所有数据

水平原因可能存在诸多问题,希望指出,谢谢

猜你喜欢

转载自blog.csdn.net/Hello_Ray/article/details/109134775