1. 程式人生 > 實用技巧 >Mongodb千萬級資料在python下的綜合壓力測試及應用探討

Mongodb千萬級資料在python下的綜合壓力測試及應用探討

曾經在收集資料的專案中,用過mongodb的資料儲存,但是當資料很大的時候,還是比較的吃力。很可能當時的應用水平不高,也可以是當時的伺服器不是很強。 所以這次能力比以前高點了,然後伺服器比以前也高端了很多,好嘞 ~再測試下。

(更多的是單機測試,沒有用複製分片的測試 ~)!


相比較MySQLMongoDB資料庫更適合那些讀作業較重的任務模型MongoDB能充分利用機器的記憶體資源。如果機器的記憶體資源豐富的話,MongoDB的查詢效率會快很多。


這次測試的伺服器是dell 的 r510!

235621353.jpg

記憶體還行,是48G的,本來想讓同事給加滿,但是最終還是沒有說出口 ~

235739466.jpg磁碟是10個2T的,但是因為格式化的時間太久了,哥們直接把其他的硬碟給拔出來了,就用了三個盤。。。data目錄沒有做raid,是為了讓他們體現更好的硬碟速度。

235930831.jpg


既然說好了是在python下的應用測試,那就需要安裝mongodb python下的模組 !

對了,不知道mongodb-server的安裝要不要說下?

cat/etc/yum.repos.d/10.repo
[10gen]
name=10genRepository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64
gpgcheck=0


000408806.jpg

Pymongo的基本用法


frompymongoimport*#導包
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)#切片


測試的程式碼:

#!/usr/bin/envpython
frompymongoimportConnection
importtime,datetime
importos,sys
connection=Connection('127.0.0.1',27017)
db=connection['xiaorui']
deffunc_time(func):
def_wrapper(*args,**kwargs):
start=time.time()
func(*args,**kwargs)
printfunc.__name__,'run:',time.time()-start
return_wrapper
@func_time
defainsert(num):
posts=db.userinfo
forxinrange(num):
post={"_id":str(x),
"author":str(x)+"Mike",
"text":"Myfirstblogpost!",
"tags":["xiaorui","xiaorui.cc","rfyiamcool.51cto"],
"date":datetime.datetime.utcnow()}
posts.insert(post)
if__name__=="__main__":
num=sys.argv[1]
ainsert(int(num))

咱們就先來個百萬的資料做做測試~

綜合點的資料:

002508131.jpg

在top下看到的程式佔用資源的情況 ~ 我們看到的是有兩個程序的很突出,對頭 ! 正是mongodb的服務和我們正在跑的python指令碼 !

002605855.jpg


看下服務的io的情況 ~

002741677.jpg


指令碼執行完畢,總結下執行的時間 ~

002816300.jpg


檢視mongodb的狀態~

他的insert也不到5k ~ 插入量也就800k左右 ~

它的輸出有以下幾列:


inserts/s 每秒插入次數

query/s 每秒查詢次數

update/s 每秒更新次數

delete/s 每秒刪除次數

getmore/s 每秒執行getmore次數

command/s 每秒的命令數,比以上插入、查詢、更新、刪除的綜合還多,還統計了別的命令

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 時間戳

瞅下面的監控資料 !

003310317.jpg

然後我們在測試下在一千萬的資料下的消耗時間情況 ~

共用了2294秒,每秒插入 4359個數據 ~

010140526.jpg看看他的記憶體的使用情況:

虛擬記憶體在8gb左右,真實記憶體在2gb左右

010245223.jpg

再換成多執行緒的模式跑跑 ~ 個人不太喜歡用多執行緒,這東西屬於管你忙不忙,老大說了要公平,我就算搶到了,但是沒事幹,我也不讓給你。。。屬於那種蠻幹的機制 ~


nima,要比單個跑的慢呀 ~ 執行緒這東西咋會這麼不靠譜呀 ~

應該是沒有做執行緒池pool,拉取佇列。導致執行緒過多導致的。不然不可能比單程序都要慢~

還有就是像這些涉及到IO的東西,交給協程的事件框架更加合理點 !!!


defgoodinsert(a):
posts.insert(a)
defainsert(num):
forxinrange(num):
post={"_id":str(x),
"author":str(x)+"Mike",
"text":"Myfirstblogpost!",
"tags":["mongodb","python","pymongo"],
"date":datetime.datetime.utcnow()}
#goodinsert(post)
a=threading.Thread(target=goodinsert,args=(post,))
a.start()


023536618.jpg


python畢竟有gil的限制,雖然multiprocess號稱可以解決多程序的。但是用過的朋友知道,這個東西更不靠譜 ~ 屬於坑人的東西 ~

要是有朋友懷疑是python的單程序的效能問題,那咱們就用supervisord跑了幾個後臺的python壓力指令碼 ~ supervisord的配置我就不說了,我以前的文章裡面有詳述的 ~

015034609.jpg

cpu方面是跑的有點均勻了,但是mongodb那邊的壓力總是上不去

當加大到16個後臺程序做壓力測試的時候 ~ 大家會發現insert很不穩定。 看來他的極限也就是2MB左右的資料 ~

015448335.jpg

當減少到8個壓力程序的時候 ~ 我們發現他的insert慢慢的提供到正常了,也就是說 他真的是2MB的極限 ~

015559242.jpg

腳本里面是有做有序的id插入的,我們試試把id的插入給去掉,看看有沒有提升~

結果和不插入id差不多的結果 ~

085221687.jpg

調優之後~ 再度測試

ulimit的優化

cat/etc/security/limits.conf
*softnofile102400
*hardnofile102400


核心的tcp優化

cat/etc/sysctl.conf
net.ipv4.tcp_syncookies=1
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_timestsmps=0
net.ipv4.tcp_synack_retries=2
net.ipv4.tcp_syn_retries=2
net.ipv4.tcp_wmem=8192436600873200
net.ipv4.tcp_rmem=32768436600873200
net.ipv4.tcp_mem=945000009150000092700000
net.ipv4.tcp_max_orphans=3276800
net.ipv4.tcp_fin_timeout=30
#直接生效
/sbin/sysctl-p

啟動的時候,加上多核的優化引數

多核問題可以在啟動時加入啟動引數:numactl--interleave=all


insert的頻率已經到了2w左右 ~ 記憶體佔用了8G左右 ~


143629965.png

143630829.png



我想到的一個方案:

當然不用非要celery,就算咱們用socket寫分發,和zeromq的pub sub也可以實現這些的。這是celery的排程更加專業點。

021114653.jpg

剛才我們測試的都是insert,現在我們再來測試下在千萬級別資料量下的查詢如何:

查詢正則的,以2開頭的字元

posts=db.userinfo
foriinposts.find({"author":re.compile('^2.Mike')}):
printi


013606877.jpg

精確的查詢:

查詢在5s左右 ~

013944841.jpg


225244177.jpg225244313.jpg


225244670.jpg

225244798.jpg


總結:

典型的高讀低寫資料庫 !





轉載於:https://blog.51cto.com/rfyiamcool/1329351