背景說明
近期北京理財頻道反饋用來存放股市實時數據的MongoDB數據庫寫響應請求很慢,難以跟上業務寫入速度水平。我們分析了線上現場的情況,發現去年升級到SSD磁盤后,數據持久化的磁盤IO開銷已經不是瓶頸.通過日志分析,線上單次寫入(更新)請求大多在數十毫秒這個級別,數據庫端觀察幾個主要的db在繁忙時通常有95%以上的時間在進行鎖等待。線上數據庫并發很高,接近1000個連接,所以懷疑是并發爭用表鎖導致性能不足。
我們知道MongoDB的mmap存儲引擎一直是庫/表級鎖,因此任何寫操作并發越高鎖爭用造成的性能損耗越大。為了改善鎖并發性能MongoDB,升級到行級鎖引擎應該能夠改善線上更新數據的性能瓶頸。3.0的WT存儲引擎和toku開發的tokumx存儲引擎都號稱實現了行級鎖和多版本并發控制。因此,為了確定我們升級的方向,決定使用線上類似的場景,對三種存儲引擎進行一次性能測試,評估最能改善并發更新寫的方案。
我們取得了線上最繁忙的stock和stock_status數據,并且仿照線上并發更新最頻繁的根據證券code更新的方式,在測試環境進行測驗。
硬件環境
CPU: 24 核 Intel(R) Xeon(R) CPU E5-2630 0 @ 2.30GHz
內存: 48G
磁盤: SSD
MongoDB版本
1. Mmap存儲引擎 MongoDB-2.6.9
2. Toku存儲引擎 MongoDB-2.4.10
3. WiredTiger存儲引擎 MongoDB 3.0.5
測試用例
從線上將股票信息表數據導入測試環境,創建與線上一致的索引,股票碼code_id為唯一索引。
單純寫測試:從股票表stock中抽取1000個code_id,用隨機函數獲取其中一個code_id,對這一行數據進行一次update操作;
讀寫混合測試:在一定并發度的寫操作情況下,以同樣并發度通過code_id讀取一行數據,讀寫混合比例為1:1。
測試腳本
1.寫測試腳本
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import multiprocessing
import time
import random
import pymongo
client = pymongo.MongoClient("172.17.1.234", 27017)
db = client.stock
def get_id():
code_list = [1000個code_id]
code_loct = random.randint(0, 999)
up_value = random.randint(10, 99)/10.0
return code_list[code_loct], up_value
def update_func():
while True:
code_id, up_value = get_id()
db.stock.update_one({"CODE":str(code_id)},{"$set":
{"ASK1":str(up_value),"ASK2":str(up_value),"ASK3":str(up_value),"ASK4":str(up_value),"ASK5":str(up_value),"ASKVOL1":str(up_value),"ASKVOL2":str(up_value),"ASKVOL3":str(up_value),"ASKVOL4":str(up_value),"ASKVOL5":str(up_value),"BID1":str(up_value),"BID2":str(up_value),"BID3":str(up_value),"BID4":str(up_value),"BID5":str(up_value)}})
if __name__ == "__main__":
pool = multiprocessing.Pool(processes=并發度)
for i in xrange(10000000):
pool.apply_async(update_func,)
pool.close()
pool.join()
print "\n" print "All done."
2.讀測試腳本
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import multiprocessing
import time
import random
import pymongo
client = pymongo.MongoClient("172.17.1.234", 27017)
db = client.stock
def get_id():
code_list = [1000個code_id]
code_loct = random.randint(0, 499)
return code_list[code_loct]
def update_func():
while True:
code_id = get_id()
db.stock.find_one({"CODE":str(code_id)},{"CODE":1,"ASK1":1,"ASK2":1})
if __name__ == "__main__":
pool = multiprocessing.Pool(processes=并發度)
for i in xrange(1000000):
pool.apply_async(update_func,)
pool.close()
pool.join()
print "\n"
print "All done."
測試結果
1.單純寫的測試結果
結論:WiredTiger在純update測試場景中性能明顯高于toku和mmap
a.toku和mmap并發度超過32后TPS穩定在1.4萬到1.5萬左右,此時整體DB的鎖爭用非常高
b.WiredTiger表現良好,128并發度時TPS處理能力達到5萬多,更高并發下處理能力逐漸下降,穩定在3萬到4萬之間

2.讀寫1比1混合的測試結果
結論:WiredTiger在讀寫1比1混合測試場景中,綜合能力優于toku和mmap,且讀寫互不影響,都比較穩健
a.WiredTiger在讀寫混合測試場景中更新性能明顯高于toku和mmap,讀性能在高于256時不如toku和mmap,但是讀寫互不影響且性能較為穩定
b.mmap在高并發情況下讀性能良好,但是更新性能下降很明顯,受讀的影響較大
c.toku在讀寫兩端就像是WiredTiger和mmap的中庸版
讀寫混合模式下,WiredTiger在32到256之間的并發情況下,綜合能力優于toku和mmap,其他并發度情況下讀寫綜合能力相近
小結
由測試結果可以看出,3.0的WT引擎對多并發更新的場景明顯好于其他兩種引擎,TPS性能有較大的提升,因此建議線上升級3.0并且更換存儲引擎。
目前線上已經在測試環境部署了3.0的數據庫,等待應用反饋回歸測試結果,如果一切順利,打算盡快升級
原創文章
禁止其他公眾賬號轉載
文章列表