用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! "; exit(1); } if (!$pid) { $redis = PRedis::getInstance(); // do something exit; } }
在R的doParallel下,我对多线程的理解是,针对每一个i, 系统新建一个线程。
也就意味着,在for之前的环境,子线程是拷贝过来的,不同子线程的环境一致;for之后的环境,不同子线程是各自独立的环境,调用$redis = PRedis:getInstance()应该生成的是不同的实例。
如果上面的作者说的是对的,即for之前和之后的环境,不同子线程的环境都是一致的,则多线程都是使用的同一个连接。所以会出错。(????)
问题是,这时候不应该报 too many connections的错误啊,明明就只有一个connection。
不知道怎么回事....还是找时间回头好好研究下多线程的执行原理算了。。。