nodejs + redis/mysql 連接池問題
需不需要連接池
連接池的作用主要是較少每次臨時建立連接所帶來的開銷。初步一看,nodejs運行單線程上,它不能同時使用多個連接,乍一看是不需要連接池的。但是這只是我們初步下意識的感覺,下面我們詳細分析來看看這個結論對是不對。
先從簡單的redis開始。
redis服務器也是運行在單線程上的。倆都是單線程,看起來更加堅定不需要連接池的結論了。
從詳細的圖像來看看nodejs 連接 redis用連接池有沒有意義。

上圖中,nodejs共有倆連接,分別發送查詢請求到redis服務器上。因為redis是單線程作業,不管兩個查詢任務是由一個連接發來還是多個連接發來,也不管任務是串行一前一后先后發送到服務器,還是並行的同時發送到服務器上,redis都將他們一個個按順序執行,並通過當前連接返回給客戶端(這里是nodejs)。nodejs接受到redis的返回后,也管不了並行不並行,都得等他nodejs的主線程空閑的時候才能來一個個處理服務器返回的數據。
所以單從上面結論來看,nodejs + redis只需要公用一個連接就可以了,所以是不需要用連接池的。
再來看看nodejs + mysql下的情況
不同的是mysql不是單線程服務的,也就是它可以並行處理多個查詢請求。

如上圖所示,mysql會為每個連接創建一個單獨的線程來查詢。不同於redis數據基本都在內存中,因為mysql會有大量的讀取磁盤的IO操作,所以多個線程一起工作會比一個個查詢要快。
但是nodejs又是單線程的,它能不能同時發送多個請求到mysql服務器上呢?
這里要理解nodejs的運作,雖然nodejs是一個主線程,但是它調用的IO指令等是通過另外的線程去做的,IO指令完成后就給主線程一個小任務片,也就是回調函數了。
這里有個很關鍵的點就是,nodejs主線程一個,但是IO線程會有多個。
因此如果用nodejs + mysql只用單個連接的話那么就利用不到mysql能同時服務多個查詢的優勢了。應該使用類似下圖的運作方式,nodejs 使用多個連接來連接mysql。多連接是需要連接池的,有連接池就避免了每次連接都要去創建銷毀的消耗了。

所以我們第一步的感覺認為Nodejs是單線程而不是需要連接池是錯誤的,使用不使用連接池,不光看客戶端,還要看數據庫服務器等。要全盤理解整個系統的運作模式才能下結論。了解服務器的運作模式,有沒有阻塞操作,是否是多線程等。我想redis設計成單線程主要原因在於它的數據基本都在內存中,查詢數據過程總不會產生阻塞過程,cpu也不會處於空閑狀態。
全局連接斷開重連問題
到這里還沒完。綜上面的分析,nodejs + mysql用線程池是沒什么問題的。nodejs + redis只用單個連接就夠。不過也還是有人建議需要連接池。為了說明問題得從代碼着手。我們使用node-redis這個包來做例子。
例如新建了一個db.js
var redis = require("redis"), client = redis.createClient(6379, "127.0.0.1"); module.exports = client;
上面的連接會在程序啟動載入完后就連接上了。使用它的時候引入它就可以使用全局唯一的連接。
var db = require("db.js"); exports.add = function(req, res, next) { db.get("keyName", function() { res.send("ok"); }); }
這里於是有這樣的問題存在。
但此處全局只有一個連接,並且這個連接是程序啟動的時候創建的。它不同於連接池中的連接那樣運行時候動態創建。如果某個時候唯一的連接斷掉了,程序又不會動態去創建連接,豈不是需要重新啟動服務器才行。
看是來挺可怕,但好在這個問題是不存在的。因為redis的客戶端會自動重新連接,所以不需要重新啟動服務器。
但是因為連接斷開那一小段時間,應用服務器不能正常對外服務,但是連接自動重連是需要一定的時間間隔的。例如一秒之后,所以這一秒之內系統是處在不能服務狀態。
也正是基於上面這樣的原因,於是就有連接redis使用連接池的做法。如果使用連接池來管理,當連接不可用的時候立即手動去創建新連接。和自動重連相比,一個是手動立即重連,一個是等到一定間隔重連。相對來說手動重連的時間更短,也就是說系統那1秒中不能服務的狀態或許可以縮短成0.5秒。於是就有了使用連接池管理redis連接的做法。嚴格來說這個不能算是連接池,而是一個連接管理模塊。
到這里最后的結論nodejs + mysql使用連接池更好, nodejs + redis可以使用也可以不用。
