15 mongodb 千萬級數據在 python 下的綜合壓力測試及應用探討
Python實戰-從菜鳥到大牛的進階之路 作者:極客學院 投票推薦 加入書簽 留言反饋
曾經在收集數據的項目中,用過 mongodb 的數據存儲,但是當數據很大的時候,還是比較的吃力。很可能當時的應用水平不高,也可以是當時的服務器不是很強。 所以這次能力比以前高點了,然後服務器比以前也高端了很多,好嘞 ~再測試下。
(更多的是單機測試,沒有用複製分片的測試 ~)!
相比較 mysql,mongodb 數據庫更適合那些讀作業較重的任務模型。mongodb 能充分利用機器的內存資源。如果機器的內存資源豐富的話,mongodb 的查詢效率會快很多。
這次測試的服務器是 dell 的 r510!
圖片 15.1 pic
內存還行,是 48 g 的,本來想讓同事給加滿,但是最終還是沒有說出口 ~
圖片 15.2 pic
磁盤是 10 個 2t 的,但是因為格式化的時間太久了,哥們直接把其他的硬盤給拔出來了,就用了三個盤。。。data 目錄沒有做 raid,是為了讓他們體現更好的硬盤速度。
圖片 15.3 pic
既然說好了是在 python 下的應用測試,那就需要安裝 mongodb python 下的模塊!對了,不知道 mongodb-server 的安裝要不要說下?
cat /etc/yum.repos.d/10.repo[10gen]name=10gen repositorybaseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64gpgcheck=0 </pre>
圖片 15.4 pic
pymongo 的基本用法
from pymongo import * # 導包con = connection(...) # 鏈接db = con.database # 鏈接數據庫db.authenticate(\''username\'', \''password\'') # 登錄db.drop_collection(\''users\'') #刪除表db.logout # 退出db.collection_names # 查看所有表db.users.count # 查詢數量db.users.find_one({\''name\'' : \''xiaoming\''}) # 單個對象db.users.find({\''age\'' : 18}) # 所有對象db.users.find({\''id\'':64}, {\''age\'':1,\''_id\'':0}) # 返回一些字段 默認_id總是返回的 0不返回 1返回db.users.find({}).sort({\''age\'': 1}) # 排序db.users.find({}).skip(2).limit(5) # 切片 </pre>
測試的代碼:
#!/usr/bin/env pythonfrom pymongo import connectionimport time,datetimeimport os,sysconnection = connection(\''127.0.0.1\'', 27017)db = connection[\''xiaorui\'']def func_time(func): def _wrapper(*args,**kwargs):start = time.timefunc(*args,**kwargs)print func.__name__,\''run:\'',time.time-start return _wrapper@func_timedef ainsert(num): posts = db.userinfo for x in range(num):post = {"_id" : str(x),"author": str(x)+"mike","text": "my first blog post!","tags": ["xiaorui", "xiaorui", "rfyiamcool.51cto"],"date": datetime.datetime.uow}posts.insert(post)if __name__ == "__main__": num = sys.argv[1] ainsert(int(num)) </pre>
咱們就先來個百萬的數據做做測試~
綜合點的數據:
圖片 15.5 pic
在 top 下看到的程序占用資源的情況 ~ 我們看到的是有兩個進程的很突出,對頭 ! 正是 mongodb 的服務和我們正在跑的 python 腳本!
圖片 15.6 pic
看下服務的 io 的情況 ~
圖片 15.7 pic
腳本運行完畢,總結下運行的時間 ~
圖片 15.8 pic
查看 mongodb 的狀態~
他的 insert 也不到 5k ~ 插入量也就 800k 左右 ~
它的輸出有以下幾列:
inserts/s 每秒插入次數
query/s 每秒查詢次數
update/s 每秒更新次數
delete/s 每秒刪除次數
getmore/s 每秒執行 getmore 次數
flushs/s 每秒執行 fsync 將數據寫入硬盤的次數。
mapped/s 所有的被 mmap 的數據量,單位是 mb,
vsize 虛擬內存使用量,單位 mb
res 物理內存使用量,單位 mb
faults/s 每秒訪問失敗數(隻有 linux 有),數據被交換出物理內存,放到 swap。不要超過 100,否則就是機器內存太小,造成頻繁 swap 寫入。此時要升級內存或者擴展
locked % 被鎖的時間百分比,盡量控製在 50% 以下吧
idx miss % 索引不命中所占百分比。如果太高的話就要考慮索引是不是少了
q t|r|w 當 mongodb 接收到太多的命令而數據庫被鎖住無法執行完成,它會將命令加入隊列。這一欄顯示了總共、讀、寫 3 個隊列的長度,都為 0 的話表示 mongo 毫無壓力。高並發時,一般隊列值會升高。
conn 當前連接數
time 時間戳
瞅下麵的監控數據!
圖片 15.9 pic
然後我們在測試下在一千萬的數據下的消耗時間情況 ~
共用了 2294 秒,每秒插入 4359 個數據 ~
圖片 15.10 pic
看看他的內存的使用情況:
虛擬內存在 8 gb 左右,真實內存在 2 gb 左右
圖片 15.11 pic
再換成多線程的模式跑跑 ~ 個人不太喜歡用多線程,這東西屬於管你忙不忙,老大說了要公平,我就算搶到了,但是沒事幹,我也不讓給你。。。屬於那種蠻幹的機製 ~
nima,要比單個跑的慢呀 ~ 線程這東西咋會這麽不靠譜呀 ~
應該是沒有做線程池 pool,拉取隊列。導致線程過多導致的。不然不可能比單進程都要慢~
還有就是像這些涉及到 io 的東西,交給協程的事件框架更加合理點 !!!
def goodinsert(a): posts.insert(a)def ainsert(num): for x in range(num):post = {"_id" : str(x),"author": str(x)+"mike","text": "my first blog post!","tags": ["mongodb", "python", "pymongo"],"date": datetime.datetime.uow} # goodinsert(post)a=threading.thread(target=goodinsert,args=(post,))a.start </pre>
圖片 15.12 pic
python 畢竟有 gil 的限製,雖然 multiprocess 號稱可以解決多進程的。但是用過的朋友知道,這個東西更不靠譜 ~ 屬於坑人的東西 ~
要是有朋友懷疑是 python 的單進程的性能問題,那咱們就用 supervisord 跑了幾個後台的 python 壓力腳本 ~ supervisord 的配置我就不說了,我以前的文章裏麵有詳述的 ~
圖片 15.13 pic
cpu 方麵是跑的有點均勻了,但是 mongodb 那邊的壓力總是上不去
當加大到 16 個後台進程做壓力測試的時候 ~ 大家會發現 insert 很不穩定。 看來他的極限也就是 2 mb 左右的數據 ~
圖片 15.14 pic
當減少到 8 個壓力進程的時候 ~ 我們發現他的 insert 慢慢的提供到正常了,也就是說 他真的是 2 mb 的極限 ~
圖片 15.15 pic
腳本裏麵是有做有序的 id 插入的,我們試試把 id 的插入給去掉,看看有沒有提升~
結果和不插入 id 差不多的結果 ~
圖片 15.16 pic
調優之後~ 再度測試
ulimit 的優化
cat /etc/security/limits.conf* soft nofile 102400* hard nofile 102400 </pre>
內核的 tcp 優化
cat /etc/sysctl.con.ipv4.tcp_syncookies = .ipv4.tcp_tw_reuse = .ipv4.tcp_tw_recycle = .ipv4.tcp_timestsmps = .ipv4.tcp_synack_retries = .ipv4.tcp_syn_retries = .ipv4.tcp_wmem = 8192 436600 87320.ipv4.tcp_rmem = 32768 436600 87320.ipv4.tcp_mem = 94500000 91500000 9270000.ipv4.tcp_max_orphans = 327680.ipv4.tcp_fin_timeout = 30 #直接生效/sbin/sysctl -p </pre>
啟動的時候,加上多核的優化參數
多核問題可以在啟動時加入啟動參數: numactl --interleave=all </pre>
insert 的頻率已經到了 2 w 左右 ~ 內存占用了 8 g 左右 ~
圖片 15.17 pic
圖片 15.18 pic
我想到的一個方案:
當然不用非要 celery,就算咱們用 socket 寫分發,和 zeromq 的 pub sub 也可以實現這些的。這是 celery 的調度更加專業點。
圖片 15.19 pic
剛才我們測試的都是insert,現在我們再來測試下在千萬級別數據量下的查詢如何:
查詢正則的,以2開頭的字符
posts = db.userinfofor i in posts.find({"author":repile(\''^2.mike\'')}): print i </pre>
圖片 15.20 pic
精確的查詢:
查詢在 5s 左右 ~
圖片 15.21 pic
圖片 15.22 pic
圖片 15.23 pic
圖片 15.24 pic
圖片 15.25 pic
總結:
典型的高讀低寫數據庫!
本文出自 “峰雲,就她了。” 博客,謝絕轉載!
(更多的是單機測試,沒有用複製分片的測試 ~)!
相比較 mysql,mongodb 數據庫更適合那些讀作業較重的任務模型。mongodb 能充分利用機器的內存資源。如果機器的內存資源豐富的話,mongodb 的查詢效率會快很多。
這次測試的服務器是 dell 的 r510!
圖片 15.1 pic
內存還行,是 48 g 的,本來想讓同事給加滿,但是最終還是沒有說出口 ~
圖片 15.2 pic
磁盤是 10 個 2t 的,但是因為格式化的時間太久了,哥們直接把其他的硬盤給拔出來了,就用了三個盤。。。data 目錄沒有做 raid,是為了讓他們體現更好的硬盤速度。
圖片 15.3 pic
既然說好了是在 python 下的應用測試,那就需要安裝 mongodb python 下的模塊!對了,不知道 mongodb-server 的安裝要不要說下?
cat /etc/yum.repos.d/10.repo[10gen]name=10gen repositorybaseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64gpgcheck=0 </pre>
圖片 15.4 pic
pymongo 的基本用法
from pymongo import * # 導包con = connection(...) # 鏈接db = con.database # 鏈接數據庫db.authenticate(\''username\'', \''password\'') # 登錄db.drop_collection(\''users\'') #刪除表db.logout # 退出db.collection_names # 查看所有表db.users.count # 查詢數量db.users.find_one({\''name\'' : \''xiaoming\''}) # 單個對象db.users.find({\''age\'' : 18}) # 所有對象db.users.find({\''id\'':64}, {\''age\'':1,\''_id\'':0}) # 返回一些字段 默認_id總是返回的 0不返回 1返回db.users.find({}).sort({\''age\'': 1}) # 排序db.users.find({}).skip(2).limit(5) # 切片 </pre>
測試的代碼:
#!/usr/bin/env pythonfrom pymongo import connectionimport time,datetimeimport os,sysconnection = connection(\''127.0.0.1\'', 27017)db = connection[\''xiaorui\'']def func_time(func): def _wrapper(*args,**kwargs):start = time.timefunc(*args,**kwargs)print func.__name__,\''run:\'',time.time-start return _wrapper@func_timedef ainsert(num): posts = db.userinfo for x in range(num):post = {"_id" : str(x),"author": str(x)+"mike","text": "my first blog post!","tags": ["xiaorui", "xiaorui", "rfyiamcool.51cto"],"date": datetime.datetime.uow}posts.insert(post)if __name__ == "__main__": num = sys.argv[1] ainsert(int(num)) </pre>
咱們就先來個百萬的數據做做測試~
綜合點的數據:
圖片 15.5 pic
在 top 下看到的程序占用資源的情況 ~ 我們看到的是有兩個進程的很突出,對頭 ! 正是 mongodb 的服務和我們正在跑的 python 腳本!
圖片 15.6 pic
看下服務的 io 的情況 ~
圖片 15.7 pic
腳本運行完畢,總結下運行的時間 ~
圖片 15.8 pic
查看 mongodb 的狀態~
他的 insert 也不到 5k ~ 插入量也就 800k 左右 ~
它的輸出有以下幾列:
inserts/s 每秒插入次數
query/s 每秒查詢次數
update/s 每秒更新次數
delete/s 每秒刪除次數
getmore/s 每秒執行 getmore 次數
flushs/s 每秒執行 fsync 將數據寫入硬盤的次數。
mapped/s 所有的被 mmap 的數據量,單位是 mb,
vsize 虛擬內存使用量,單位 mb
res 物理內存使用量,單位 mb
faults/s 每秒訪問失敗數(隻有 linux 有),數據被交換出物理內存,放到 swap。不要超過 100,否則就是機器內存太小,造成頻繁 swap 寫入。此時要升級內存或者擴展
locked % 被鎖的時間百分比,盡量控製在 50% 以下吧
idx miss % 索引不命中所占百分比。如果太高的話就要考慮索引是不是少了
q t|r|w 當 mongodb 接收到太多的命令而數據庫被鎖住無法執行完成,它會將命令加入隊列。這一欄顯示了總共、讀、寫 3 個隊列的長度,都為 0 的話表示 mongo 毫無壓力。高並發時,一般隊列值會升高。
conn 當前連接數
time 時間戳
瞅下麵的監控數據!
圖片 15.9 pic
然後我們在測試下在一千萬的數據下的消耗時間情況 ~
共用了 2294 秒,每秒插入 4359 個數據 ~
圖片 15.10 pic
看看他的內存的使用情況:
虛擬內存在 8 gb 左右,真實內存在 2 gb 左右
圖片 15.11 pic
再換成多線程的模式跑跑 ~ 個人不太喜歡用多線程,這東西屬於管你忙不忙,老大說了要公平,我就算搶到了,但是沒事幹,我也不讓給你。。。屬於那種蠻幹的機製 ~
nima,要比單個跑的慢呀 ~ 線程這東西咋會這麽不靠譜呀 ~
應該是沒有做線程池 pool,拉取隊列。導致線程過多導致的。不然不可能比單進程都要慢~
還有就是像這些涉及到 io 的東西,交給協程的事件框架更加合理點 !!!
def goodinsert(a): posts.insert(a)def ainsert(num): for x in range(num):post = {"_id" : str(x),"author": str(x)+"mike","text": "my first blog post!","tags": ["mongodb", "python", "pymongo"],"date": datetime.datetime.uow} # goodinsert(post)a=threading.thread(target=goodinsert,args=(post,))a.start </pre>
圖片 15.12 pic
python 畢竟有 gil 的限製,雖然 multiprocess 號稱可以解決多進程的。但是用過的朋友知道,這個東西更不靠譜 ~ 屬於坑人的東西 ~
要是有朋友懷疑是 python 的單進程的性能問題,那咱們就用 supervisord 跑了幾個後台的 python 壓力腳本 ~ supervisord 的配置我就不說了,我以前的文章裏麵有詳述的 ~
圖片 15.13 pic
cpu 方麵是跑的有點均勻了,但是 mongodb 那邊的壓力總是上不去
當加大到 16 個後台進程做壓力測試的時候 ~ 大家會發現 insert 很不穩定。 看來他的極限也就是 2 mb 左右的數據 ~
圖片 15.14 pic
當減少到 8 個壓力進程的時候 ~ 我們發現他的 insert 慢慢的提供到正常了,也就是說 他真的是 2 mb 的極限 ~
圖片 15.15 pic
腳本裏麵是有做有序的 id 插入的,我們試試把 id 的插入給去掉,看看有沒有提升~
結果和不插入 id 差不多的結果 ~
圖片 15.16 pic
調優之後~ 再度測試
ulimit 的優化
cat /etc/security/limits.conf* soft nofile 102400* hard nofile 102400 </pre>
內核的 tcp 優化
cat /etc/sysctl.con.ipv4.tcp_syncookies = .ipv4.tcp_tw_reuse = .ipv4.tcp_tw_recycle = .ipv4.tcp_timestsmps = .ipv4.tcp_synack_retries = .ipv4.tcp_syn_retries = .ipv4.tcp_wmem = 8192 436600 87320.ipv4.tcp_rmem = 32768 436600 87320.ipv4.tcp_mem = 94500000 91500000 9270000.ipv4.tcp_max_orphans = 327680.ipv4.tcp_fin_timeout = 30 #直接生效/sbin/sysctl -p </pre>
啟動的時候,加上多核的優化參數
多核問題可以在啟動時加入啟動參數: numactl --interleave=all </pre>
insert 的頻率已經到了 2 w 左右 ~ 內存占用了 8 g 左右 ~
圖片 15.17 pic
圖片 15.18 pic
我想到的一個方案:
當然不用非要 celery,就算咱們用 socket 寫分發,和 zeromq 的 pub sub 也可以實現這些的。這是 celery 的調度更加專業點。
圖片 15.19 pic
剛才我們測試的都是insert,現在我們再來測試下在千萬級別數據量下的查詢如何:
查詢正則的,以2開頭的字符
posts = db.userinfofor i in posts.find({"author":repile(\''^2.mike\'')}): print i </pre>
圖片 15.20 pic
精確的查詢:
查詢在 5s 左右 ~
圖片 15.21 pic
圖片 15.22 pic
圖片 15.23 pic
圖片 15.24 pic
圖片 15.25 pic
總結:
典型的高讀低寫數據庫!
本文出自 “峰雲,就她了。” 博客,謝絕轉載!