用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。
不知道怎么回事....還是找時間回頭好好研究下多線程的執行原理算了。。。
