首页 > 代码库 > 多线程读取数据库导致连接失败解决方案

多线程读取数据库导致连接失败解决方案

问题背景:

某需求需要处理千万级别的数据,按一定规则导出,生成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 @Overview16 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     @Overview18     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-->

 

多线程读取数据库导致连接失败解决方案