问题背景:
某需求需要处理千万级别的数据,按一定规则导出,生成txt或xml文件,单线程处理太慢,写了一个跑了看差不多要处理6~10个小时。想了想还是就用多线程来从数据库中limit不同的数据来进行处理,耗时控制在5分钟以内.相关逻辑为:
1 //……其他代码 2 int count = 10000000;//假设共1千万条数据需要读取 3 int per = 50000;//每次处理5万条 4 for (int i = 0 ; i < count/per ; i ++){ //创建1000/5=200个线程来处理数据 5 //使用线程池来处理多线程 6 ThreadPool.execute(new ProcessTask(i*per,per)); 7 } 8 //……其他代码 9 class ProcessTask implements Runnable{ 10 int beigin,num; 11 public ProcessTask(int begin,int num){ 12 this.begin = begin; 13 this.num = num; 14 } 15 @Overview 16 public void run (){ 17 //线程内容,读取数据 18 List<Data> userlist = XXXService.getDataByLimit(begin,num); 19 //……对读出来的数据进行处理 20 //……其他代码 21 } 22 }
可见,此处通过循环同时创建了200个线程,而这200个线程都需要读取数据库,测试环境下最大连接数设置的是20,此时数据库就容易报错,无法连接。
除了无法连接数据库以外,还可能会导致以下问题:
- 短时间内大量占用服务器内存,导致卡顿,甚至OOM
- 在线上环境此处大量占用必定引起其他项目的性能
- 此接口恶意盗刷可能导致服务器直接宕机
解决方案:
对读取数据库操作进行sleep
1 //……其他代码 2 int count = 10000000;//假设共1千万条数据需要读取 3 int per = 50000;//每次处理5万条 4 for (int i = 0 ; i < count/per ; i ++){ //创建1000/5=200个线程来处理数据 5 //使用线程池来处理多线程 6 ThreadPool.execute(new ProcessTask(i*per,per,i)); 7 } 8 //……其他代码 9 class ProcessTask implements Runnable{ 10 int beigin,num; 11 int i ; 12 public ProcessTask(int begin,int num,int i ){ 13 this.begin = begin; 14 this.num = num; 15 this.i = i ; 16 } 17 @Overview 18 public void run (){ 19 //线程内容,读取数据 20 Thread.sleep(i*500);//可以改成更大的值 21 List<Data> userlist = XXXService.getDataByLimit(begin,num); 22 //……对读出来的数据进行处理 23 //……其他代码 24 } 25 }
这样处理后就可以分时的读取数据库,减少服务器的负担,但是时间会变长(sleep操作),200*0.5=100s,不过也是在可以接受的范围内。
感觉好像也没多少技术含量……或者说把getDataByLimit方法设置成synchronizd就行了吧,如果是单独使用的情况下……
<--EOF-->
时间: 2024-10-16 10:11:02