使用YCSB測試MongoDB的微分區效能
MongoDB的庫級鎖
MongoDB是目前最流行的NoSQL資料庫,以其自然的文檔型資料結構,靈活的資料模式以及簡單易用的水平擴充能力而獲得了很多開發人員的青睞。 但是金無足赤人無完人,MongoDB不是沒有它的一些弱點,比如說它的庫級鎖就是人們經常抱怨的一個效能瓶頸。簡單來說MongoDB的庫級鎖就是針對某一個資料庫的所有寫操作,必須在獲得這個資料庫僅有的一個互斥鎖情況下才能進行。這個聽上去很糟糕,但實際上由於一個寫操作只是針對於記憶體資料更新的那一刹那保留鎖,所以每個寫鎖的佔用時間通常是在納秒層級。正因為如此,實際應用中庫級鎖並沒有對效能產生人們所擔憂的那樣顯著的影響。
在少數超高並發寫的應用情境下,庫級鎖會可能是一個瓶頸。這個可以通過MongoDB的MMS監控裡面的DB Lock %(或者mongostat的命令列輸出)指標來進行觀察。一般情況下如果DB Lock %超過70-80%並持續就可以認為已經到飽和狀態了。如何解決這個問題呢?
方案一: 分區
這個是MongoDB的標準答案如果你有足夠的硬體資源。 分區是解決大部分效能瓶頸問題的終極方式。
方案二:分庫
這是個非常有效變通手段。具體做法就是把你的資料分到幾個不同的資料庫裡,然後在應用程式裡的資料訪問層實現一個路由切換,保證資料讀寫會被指向到相應的資料庫裡。一個比較好的例子在一個人口普查的資料庫裡,你可以為每個省建一個單獨的庫。31個資料庫組成一個邏輯大庫。但是這種做法不是什麼時候都能用的,比如說如果你需要很多整庫資料的查詢排序那樣的操作,那麼協調多個庫的結果就會顯得很麻煩或者無法實現。
方案三:等待
MongoDB 2.8 即將發布。2.8的最大改動就是把庫級鎖改成了文檔級鎖。由庫級鎖引起的效能問題應該有望得到較大改善。
方案四:微分區
微分區的定義就是使用MongoDB的分區技術,但是多個或者全部分區Mongod運行在同一台伺服器(伺服器可以是物理機或者虛機)上。由於庫級鎖的存在,以及MongoDB對多核CPU的利用率不是很高的特性,微分區在滿足以下條件的情境下會是一個不錯的效能調優手段:
1) 伺服器有多核(4或8或更多)CPU
2) 伺服器尚未出現IO瓶頸
3) 有足夠記憶體裝下熱資料(沒有出現頻繁的 page faults)
在這篇文章裡我們通過做一些效能測試來看一下使用微分區技術以後對效能提升的影響。
YCSB 效能測試工具
在開始測試之前,我想首先花點時間介紹一下YCSB這個工具。原因是很多時候我看到開發工程師或者DBA們做測試的時候往往會用一些非常簡單的工具作為用戶端進行高並發的插入或讀取測試。MongoDB本身是一個高效能的資料庫,並發量在適當調優的情況下可以達到每秒數萬級。如果用戶端的代碼是簡單粗暴型的,甚至使用單線程的用戶端,那麼效能測試的瓶頸首先就是在用戶端本身,而不是伺服器。所以選擇一個高效的用戶端是一個好的效能測試的重要的第一步。
YCSB是Yahoo開發的一個專門用來對新一代資料庫進行基準測試的工具。全名是Yahoo! Cloud Serving Benchmark。 他們開發這個工具的目的是希望有一個標準的工具用來衡量不同資料庫的效能。YCSB做了很多最佳化來提高用戶端效能,例如在資料類型上用了最原始的位元數組以減少資料對象本身建立轉換所需的時間等。YCSB的幾大特性:
* 支援常見的資料庫讀寫操作,如插入,修改,刪除及讀取
* 多線程支援。YCSB用Java實現,有很好的多線程支援。
* 靈活定義情境檔案。可以通過參數靈活的指定測試情境,如100%插入, 50%讀50%寫等等
* 資料請求分布方式:支援隨機,zipfian(只有小部分的資料得到大部分的訪問請求)以及最新資料幾種請求分布方式
* 可擴充性:可以通過擴充Workload的方式來修改或者擴充YCSB的功能
安裝YCSB
由於YCSB本身會承擔很大的工作量,一般建議部署YCSB在單獨的機器上,最好是4-8核CPU,8G記憶體以上。YCSB和資料庫伺服器最少要保證千兆的頻寬,最好是萬兆級。
* 安裝JDK 1.7
* 下載實現了MongoDB驅動的YCSB編譯版:
資源套件:
------------------------------------------分割線------------------------------------------
FTP地址:ftp://ftp1.bkjia.com
使用者名稱:ftp1.bkjia.com
密碼:www.bkjia.com
在 2015年LinuxIDC.com\7月\使用YCSB測試MongoDB的微分區效能
下載方法見
------------------------------------------分割線------------------------------------------
* 解壓縮
* 進入到ycsb目錄並運行(本地要有一個Mongo資料庫在 27017連接埠上):
./bin/ycsb run mongodb -P workloads/workloada
* 如果YCSB可以運行則表明安裝成功
你也可以用Git把源檔案拉下來自己編譯。需要JDK和Maven工具。Github地址是:https://github.com/achille/YCSB 可以參考這個頁面進行編譯安裝YCSB: https://github.com/achille/YCSB/tree/master/mongodb
YCSB情境檔案
使用YCSB測試不同情境只需要提供不同的情境檔案就可以。YCSB會按照你的情境檔案的屬性而自動產生響應的用戶端請求。在我們這次測試裡我們會使用到幾種情境:
情境S1: 100%插入。用來載入測試資料
情境S2: 寫多讀少 90% 更新 10%讀
情境S3: 混合讀寫 65%讀, 25% 插入, 10% 更新
情境S4: 讀多寫少 90% 讀, 10% 插入、更新
情境S5: 100%讀
如下是其中情境檔案S2的內容:
recordcount=5000000
operationcount=100000000
workload=com.yahoo.ycsb.workloads.CoreWorkload
readallfields=true
readproportion=0.1
updateproportion=0.9
scanproportion=0
insertproportion=0
requestdistribution=uniform
insertorder=hashed
fieldlength=250
fieldcount=8
mongodb.url=mongodb://192.168.1.2:27017
mongodb.writeConcern=acknowledged
threadcount=32
一些說明:
* 測試資料包括500萬個文檔(recordcount)
* 每個文檔大小大約2KB(fieldlength x fieldcount)。資料總共大小是10G+600M的索引
* MongoDB資料庫的url是192.168.1.2:27017
* MongoDB的寫安全設定(mongodb.writeConcern)是acknowledged
* 線程數是32(threadcount)
* 插入文檔的順序:雜湊/隨機 (insertorder)
* 更新操作: 90% (0.9)
* 讀操作: 10% (0.1)
下載所有情境檔案(S1 – S5)(見上面的幫客之家) 並解壓到上面建立的ycsb目錄下面:
MongoDB配置
本次測試是在AWS的虛擬機器主機上進行測試的。以下是伺服器配置情況:
* OS: Amazon Linux (和CentOS基本類似)
* CPU: 8 vCPU
* RAM: 30G
* Storage: 160G SSD
* Journal: 25G EBS with 1000 PIOPS
* Log: 10G EBS with 250 IOPS
*
* MongoDB: 2.6.0
* Readahead:32
幾點說明:
MongoDB的資料,恢複日誌(journal)以及系統日誌(log)分別用了3個不同的儲存盤。這是一個常見的最佳化方式,以保證寫日誌的操作不會影響到資料的刷盤IO。另外伺服器的readahead設定改到了推薦的32。關於readahead等可以參見:
單機基準測試
在我們測試使用微分區效能之前我們首先需要得出單機的最高效能。啟動目標MongoDB伺服器,登入上去後先刪除ycsb資料庫(如果已經存在)
# mongo
> use ycsb
> db.dropDatabase()
情境S1: 資料插入
接下來開始運行YCSB。進到ycsb目錄下,運行以下命令(確認目前的目錄下已經有情境檔案S1, S2, S3, S4,S5)
./bin/ycsb load mongodb -P S1 -s
如果運行正常,你會看到每隔10秒YCSB列印一下目前狀態,包括每秒的並發率以及平均回應時間。 如:
Loading workload…
Starting test.
0 sec: 0 operations;
mongo connection created with localhost:27017/ycsb
10 sec: 67169 operations; 7002.16 current ops/sec; [INSERT AverageLatency(us)=4546.87]
20 sec: 151295 operations; 7909.24 current ops/sec; [INSERT AverageLatency(us)=3920.9]
30 sec: 223663 operations; 7235.35 current ops/sec; [INSERT AverageLatency(us)=4422.63]
在啟動並執行同時你可以用mongostat(或者更好的選擇:MMS)來監控MongoDB的即時指標,看是否和YCSB的報告大體一致。
運行結束後可以看到類似於如下輸出:
[OVERALL], RunTime(ms), 687134.0
[OVERALL], Throughput(ops/sec), 7295.168457372555
…
[INSERT], Operations, 5000000
[INSERT], AverageLatency(us), 4509.1105768
[INSERT], MinLatency(us), 126
[INSERT], MaxLatency(us), 3738063
[INSERT], 95thPercentileLatency(ms), 10
[INSERT], 99thPercentileLatency(ms), 37
[INSERT], Return=0, 5000000
…
這個輸出告訴我們插入了500萬條記錄, 耗時687秒,平均並發量每秒7295條,平均回應時間4.5ms。注意這個數值本身來說對於MongoDB的效能指標沒有任何參考價值。如果你的環境有任意一點不一致,或者插入資料的大小,或者索引的多少不一樣,都會導致結果很大的不同。所以這個值只能在作為本次測試和微分區效能比較的基準值。
在MongoDB方面,要特別注意一下mongostat或者MMS彙報的page faults,network,DB Lock %等指標。如果你的network是1Gb/s 而mongostat彙報了100m的數字,那你的網路就基本是飽和了。1Gb/s的頻寬也就是128m/s的傳輸速率。在我的這個測試裡network in保持在14-15m/s的樣子,和每秒的並發率及文檔大小(7300x2KB)是一致的。
為了找到一個比較理想的用戶端線程數,我對同樣的操作重複了多次,每一次修改了情境檔案裡面的threadcount數值。測試的結果發現到了30 個線程左右並發量就到達了最高值。再增加線程數量效能不再提高。因為我的情境檔案中的threadcount值設為32。
現在我們已經在資料庫內有了500萬測試資料,現在我們可以測一下其他的幾個情境。注意:YCSB的第一個參數是測試階段。剛才是資料匯入所以第一個參數是”load”。匯入完資料後接下來就是運行階段所以第二個參數都是”run”。
情境S2: 寫多讀少
命令:
./bin/ycsb run mongodb -P S2 -s
結果
…
[OVERALL], Throughput(ops/sec), 12102.2928384723
情境S3: 混合讀寫(65%read)
命令:
./bin/ycsb run mongodb -P S3 -s
結果
…
[OVERALL], Throughput(ops/sec), 15982.39239483840
情境S4: 讀多寫少
命令:
./bin/ycsb run mongodb -P S4 -s
結果
…
[OVERALL], Throughput(ops/sec), 19102.39099223948
情境S5: 100% 讀
命令:
./bin/ycsb run mongodb -P S5 -s
結果
…
[OVERALL], Throughput(ops/sec), 49020.29394020022
微分區測試
剛才我們已經得到了單機在5個情境下的效能指標。接下來我們可以開始測試在微分區以及不同數量微分區的情景下的效能指標。
首先我們停掉單機上MongoDB資料庫。
接下來我們要建一個分區叢集。在這裡讓我要推薦給大家一個非常方便的MongoDB工具:mtools https://github.com/rueckstiess/mtools
mtools是幾個MongoDB相關工具的集合,其中的mlaunch可以協助我們不費吹灰之力地在單機上建立複製集或分區叢集。
安裝mtools(需要Python以及Python的包管理工具pip 或者easy_install):
# pip install mtools
或
# easy_install mtools
然後建一個新的目錄並在新目錄下建立微分區叢集:
# mkdir shard2
# cd shard2
# mlaunch –sharded 2 –single
這個命令會在同一台機器上建立一下4個進程:
* 1 個mongos 在27017 連接埠
* 1 個設定管理員的mongod 在27020連接埠
* 2 個分區伺服器的mongod 在27018和27019連接埠
這四個進程組成了具有兩個分區的微分區叢集。值得指出的是雖然我們已經搭建了一個分區叢集,在這個時候所有的資料還是只會去到其中一個分區,這個分區叫做主分區。要讓MongoDB把資料分布到各個分區上,必須顯式地啟用需要分區的資料庫以及集合名。
# mongo
mongos> sh.enableSharding(“ycsb”)
{ “ok” : 1 }
mongos> sh.shardCollection(“ycsb.usertable”, {_id:”hashed”})
{ “collectionsharded” : “ycsb.usertable”, “ok” : 1 }
上述兩個命令分別啟用了 ”ycsb“ 資料庫以及庫內 “usertable”集合的分區功能。在對集合開啟分區的時候還需要指定分區鍵。在這裡我們使用了 {_id: “hashed” } 表示使用 _id 欄位的雜湊值作為分區鍵。雜湊值分區鍵對大量寫的情境比較合適,可以把寫操作均勻的分布到各個分區上。
接下來我們可以按順序運行以下5個情境並收集測試結果(注意ycsb的第一個參數):
./bin/ycsb load mongodb -P S1 -s
./bin/ycsb run mongodb -P S2 -s
./bin/ycsb run mongodb -P S3 -s
./bin/ycsb run mongodb -P S4 -s
./bin/ycsb run mongodb -P S5 -s
測試完以後要用下述命令關掉整個叢集:
# mlaunch stop
以次類推,可以對4個,6個,和8個成員的微分區叢集分別建立單獨的目錄並重複5個情境的測試。如下是所有測試結果:
結論
從上表我們可以得出以下結論
* 微分區在合適的應用情境下可以顯著的提高MongoDB並發量
* 微分區對唯讀應用情境沒有任何協助
* 微分區對混合讀寫的情境(也是實際中最常見的情境)的最佳化最好:275%
* 6個微分區就已經基本到了飽和狀態,再增加更多分區已經沒有明顯改善。 這個數字可能會因人而異。
MongoDB 3.0 正式版發布下載
CentOS編譯安裝MongoDB
CentOS 編譯安裝 MongoDB與mongoDB的php擴充
CentOS 6 使用 yum 安裝MongoDB及伺服器端配置
Ubuntu 13.04下安裝MongoDB2.4.3
MongoDB入門必讀(概念與實戰並重)
Ubunu 14.04下MongoDB的安裝指南
《MongoDB 權威指南》(MongoDB: The Definitive Guide)英文文字版[PDF]
Nagios監控MongoDB分區叢集服務實戰
基於CentOS 6.5作業系統搭建MongoDB服務
MongoDB 的詳細介紹:請點這裡
MongoDB 的:請點這裡
本文永久更新連結地址: