[数据库] 多线程连接数据库报错问题

用R语言使用多线程对数据库进行批量插入操作。脚本在windows下运行正常,但是放到linux服务器上跑的时候,就出现了错误。

错误信息如下:

 task 63246 failed - "Failed to connect to database: Error: Can't connect to MySQL server on '192.168.01.30' (107)

有点懵逼。

猜想可能是数据库连接数过大的原因。

但并不清楚windows和linux下什么差异会导致一个正常,一个不正常的情况。

 

如果是使用多线程造成的错误,想来这个问题,应该很多人会碰到。

网上搜了一下,大致想到三个可能的方案:

1. 增加数据库最大连接数量。

这不一定奏效,具体看第三点。如果根本原因如第三点所说,那就不是数据库最大连接数的限制了。

因为更改后可能要重启服务器,懒,所以没有实际验证。

另外感觉这个问题应该是通过从代码角度去改善的,一味修改服务器的配置的话,随着任务数的增加,终究还是会碰到瓶颈。

这里只是提出一个想法。尽管我认为非常low,不过万不得已的时候也可以试试。

2. 在代码里增加错误控制。

  这个问题在其他语言下面应该是一个比较好实现的方式。

  只是在R语言下,让我有点无从下手。原因在于R语言的异常处理有点low? 例如如何判断连接是否存在,对R语言来说好像不知从何实现?

3. 参考:【php爬虫】百万级别知乎用户数据爬取与分析 -> 使用PHP的pcntl扩展实现多进程 -> 多进程编程中Redis和MySQL连接问题

  看不太明白它的解决方案。只是感觉应该是跟我一样的问题。

  这里贴一下原文:

根本原因是在各个子进程创建时,就已经继承了父进程一份完全一样的拷贝。对象可以拷贝,但是已创建的连接不能被拷贝成多个,由此产生的结果,就是各个进程都使用同一个redis连接,各干各的事,最终产生莫名其妙的冲突。

解决方法:

程序不能完全保证在fork进程之前,父进程不会创建redis连接实例。因此,要解决这个问题只能靠子进程本身了。试想一下,如果在子进程中获取的实例只与当前进程相关,那么这个问题就不存在了。于是解决方案就是稍微改造一下redis类实例化的静态方式,与当前进程ID绑定起来。

  具体代码可以跳转原文链接。

 ----------------------160510 17:21 更新--------------------------------

  关于方案3,仔细看了下他的代码。发现之前我对多线程包的执行方式或许理解有误。

  考虑如下代码:

for ($i = 0; $i < 10; $i++) {
          $pid = pcntl_fork();
          if ($pid == -1) {
               echo "Could not fork!\n";
               exit(1);
          }
          if (!$pid) {
               $redis = PRedis::getInstance();
               // do something     
               exit;
          }
     }

在R的doParallel下,我对多线程的理解是,针对每一个i, 系统新建一个线程。

也就意味着,在for之前的环境,子线程是拷贝过来的,不同子线程的环境一致;for之后的环境,不同子线程是各自独立的环境,调用$redis = PRedis:getInstance()应该生成的是不同的实例。

如果上面的作者说的是对的,即for之前和之后的环境,不同子线程的环境都是一致的,则多线程都是使用的同一个连接。所以会出错。(????)

问题是,这时候不应该报 too many connections的错误啊,明明就只有一个connection。

不知道怎么回事....还是找时间回头好好研究下多线程的执行原理算了。。。

 

posted @ 2016-05-10 14:54  oDoraemon  阅读(2679)  评论(0编辑  收藏  举报