對MongoDB有所瞭解的人都知道,MongoDB有一個讓人頭疼的全域鎖(讀寫鎖,允許並發讀,而寫會阻塞所有的讀寫),要命的是這個鎖不是表級的,不是庫級的,而是整個Server層級的,這讓人聽起來是不是非常的蛋疼。
在2.0版本以前,這一問題一直沒有得到解決,於是有人提出,在可預見某個update操作的記錄可能在磁碟上時,為了減少寫鎖佔用的時間,可以採用先讀後寫的方式,通過先讀一次,將要操作的記錄載入到記憶體中,再進行記憶體中的update,這樣寫鎖就不包括將資料從磁碟載入到記憶體的時間了。
在可預見資料冷熱的情況下,這種操作能夠有一定的效果,但是很明顯,這種變態的方法不應該是一個終極解決方案。
值得慶幸的是,在2.0版本中,MongoDB宣稱有很大程度的並發效能提升,而這一提升的基礎正是解決了這個全域鎖的問題。
解決的方法並不是通過減少鎖粒度來解決,雖然collection層級的鎖機制也正在開發中。(SERVER-1240)
解決方案是通過對一些可能造成長時間鎖佔用的操作進行鎖抑制。比如和我們上面的方法類似,在進行update操作時,如果發現需要更新的記錄在磁碟上,那麼這個鎖就不會一直佔用,而是等到將資料從磁碟載入到記憶體後再添加寫鎖進行update。
而同理,對於其它一些可能耗時比較長的操作也可以採用類似的方法,通過將長時間佔用的全域鎖拆分成多個細粒度的小鎖來使需要擷取鎖來進行的操作能夠交錯的執行,從而避免一夫當關萬夫莫開的情況,主要包括下面一些操作:
- 查詢操作
- 批次更新操作
- 大量刪除操作
- 批量insert寫入操作
如果你還在使用2.0以前的版本,並且在並發效能上出現問題,可以考慮在2.0.x版本上進行一些效能測試並對你的MongoDB進行升級。
2.0版本中的改進聽起來是可行的,然而他的實際效果如何呢,這裡有兩張效能測試圖片,來自blog.pythonisito.com的測試。
第一張是在不觸發page faulting的情況下1.8版本和2.0版本在加了寫鎖後的效能對比,可以看出,在寫操作能夠直接在記憶體中完成的情況下,二者的效能幾乎沒有差別。
第二張圖是在寫操作會觸發page faulting的情況,也就是在1.8中,加寫鎖的時間會包括將資料從磁碟講到記憶體的時間,而在2.0中不包括這段時間。可以看出2.0版本中情況好了很多,因為採用了鎖抑制策略,使得產生page faulting後還能有較好並發效能。