应用场景
数据库里有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增量快速遍历整张表的所有数据。
水平原因可能存在诸多问题,希望指出,谢谢