曾經(jīng)在收集數(shù)據(jù)的項(xiàng)目中,用過(guò) mongodb 的數(shù)據(jù)存儲(chǔ),但是當(dāng)數(shù)據(jù)很大的時(shí)候,還是比較的吃力。很可能當(dāng)時(shí)的應(yīng)用水平不高,也可以是當(dāng)時(shí)的服務(wù)器不是很強(qiáng)。 所以這次能力比以前高點(diǎn)了,然后服務(wù)器比以前也高端了很多,好嘞 ~再測(cè)試下。
(更多的是單機(jī)測(cè)試,沒(méi)有用復(fù)制分片的測(cè)試 ~)!
相比較 MySQL,MongoDB 數(shù)據(jù)庫(kù)更適合那些讀作業(yè)較重的任務(wù)模型。MongoDB 能充分利用機(jī)器的內(nèi)存資源。如果機(jī)器的內(nèi)存資源豐富的話(huà),MongoDB 的查詢(xún)效率會(huì)快很多。
這次測(cè)試的服務(wù)器是 dell 的 r510!
http://wiki.jikexueyuan.com/project/python-actual-combat/images/12.jpg" alt="pic" />
內(nèi)存還行,是 48 G 的,本來(lái)想讓同事給加滿(mǎn),但是最終還是沒(méi)有說(shuō)出口 ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/13.jpg" alt="pic" />
磁盤(pán)是 10 個(gè) 2T 的,但是因?yàn)楦袷交臅r(shí)間太久了,哥們直接把其他的硬盤(pán)給拔出來(lái)了,就用了三個(gè)盤(pán)。。。data 目錄沒(méi)有做 raid,是為了讓他們體現(xiàn)更好的硬盤(pán)速度。
http://wiki.jikexueyuan.com/project/python-actual-combat/images/14.jpg" alt="pic" />
既然說(shuō)好了是在 python 下的應(yīng)用測(cè)試,那就需要安裝 mongodb python 下的模塊! 對(duì)了,不知道 mongodb-server 的安裝要不要說(shuō)下?
cat /etc/yum.repos.d/10.repo
[10gen]
name=10gen Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64
gpgcheck=0
http://wiki.jikexueyuan.com/project/python-actual-combat/images/15.jpg" alt="pic" />
Pymongo 的基本用法
from pymongo import * # 導(dǎo)包
con = Connection(...) # 鏈接
db = con.database # 鏈接數(shù)據(jù)庫(kù)
db.authenticate('username', 'password') # 登錄
db.drop_collection('users') #刪除表
db.logout() # 退出
db.collection_names() # 查看所有表
db.users.count() # 查詢(xún)數(shù)量
db.users.find_one({'name' : 'xiaoming'}) # 單個(gè)對(duì)象
db.users.find({'age' : 18}) # 所有對(duì)象
db.users.find({'id':64}, {'age':1,'_id':0}) # 返回一些字段 默認(rèn)_id總是返回的 0不返回 1返回
db.users.find({}).sort({'age': 1}) # 排序
db.users.find({}).skip(2).limit(5) # 切片
測(cè)試的代碼:
#!/usr/bin/env python
from pymongo import Connection
import time,datetime
import os,sys
connection = Connection('127.0.0.1', 27017)
db = connection['xiaorui']
def func_time(func):
def _wrapper(*args,**kwargs):
start = time.time()
func(*args,**kwargs)
print func.__name__,'run:',time.time()-start
return _wrapper
@func_time
def 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.cc", "rfyiamcool.51cto"],
"date": datetime.datetime.utcnow()}
posts.insert(post)
if __name__ == "__main__":
num = sys.argv[1]
ainsert(int(num))
咱們就先來(lái)個(gè)百萬(wàn)的數(shù)據(jù)做做測(cè)試~
綜合點(diǎn)的數(shù)據(jù):
http://wiki.jikexueyuan.com/project/python-actual-combat/images/16.jpg" alt="pic" />
在 top 下看到的程序占用資源的情況 ~ 我們看到的是有兩個(gè)進(jìn)程的很突出,對(duì)頭 ! 正是 mongodb 的服務(wù)和我們正在跑的 python 腳本!
http://wiki.jikexueyuan.com/project/python-actual-combat/images/17.jpg" alt="pic" />
看下服務(wù)的 io 的情況 ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/18.jpg" alt="pic" />
腳本運(yùn)行完畢,總結(jié)下運(yùn)行的時(shí)間 ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/19.jpg" alt="pic" />
查看 mongodb 的狀態(tài)~
他的 insert 也不到 5k ~ 插入量也就 800k 左右 ~
它的輸出有以下幾列:
inserts/s 每秒插入次數(shù)
query/s 每秒查詢(xún)次數(shù)
update/s 每秒更新次數(shù)
delete/s 每秒刪除次數(shù)
getmore/s 每秒執(zhí)行 getmore 次數(shù)
command/s 每秒的命令數(shù),比以上插入、查找、更新、刪除的綜合還多,還統(tǒng)計(jì)了別的命令
flushs/s 每秒執(zhí)行 fsync 將數(shù)據(jù)寫(xiě)入硬盤(pán)的次數(shù)。
mapped/s 所有的被 mmap 的數(shù)據(jù)量,單位是 MB,
vsize 虛擬內(nèi)存使用量,單位 MB
res 物理內(nèi)存使用量,單位 MB
faults/s 每秒訪問(wèn)失敗數(shù)(只有 Linux 有),數(shù)據(jù)被交換出物理內(nèi)存,放到 swap。不要超過(guò) 100,否則就是機(jī)器內(nèi)存太小,造成頻繁 swap 寫(xiě)入。此時(shí)要升級(jí)內(nèi)存或者擴(kuò)展
locked % 被鎖的時(shí)間百分比,盡量控制在 50% 以下吧
idx miss % 索引不命中所占百分比。如果太高的話(huà)就要考慮索引是不是少了
q t|r|w 當(dāng) Mongodb 接收到太多的命令而數(shù)據(jù)庫(kù)被鎖住無(wú)法執(zhí)行完成,它會(huì)將命令加入隊(duì)列。這一欄顯示了總共、讀、寫(xiě) 3 個(gè)隊(duì)列的長(zhǎng)度,都為 0 的話(huà)表示 mongo 毫無(wú)壓力。高并發(fā)時(shí),一般隊(duì)列值會(huì)升高。
conn 當(dāng)前連接數(shù)
time 時(shí)間戳
瞅下面的監(jiān)控?cái)?shù)據(jù)!
http://wiki.jikexueyuan.com/project/python-actual-combat/images/20.jpg" alt="pic" />
然后我們?cè)跍y(cè)試下在一千萬(wàn)的數(shù)據(jù)下的消耗時(shí)間情況 ~
共用了 2294 秒,每秒插入 4359 個(gè)數(shù)據(jù) ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/21.jpg" alt="pic" />
看看他的內(nèi)存的使用情況:
虛擬內(nèi)存在 8 gb 左右,真實(shí)內(nèi)存在 2 gb 左右
http://wiki.jikexueyuan.com/project/python-actual-combat/images/22.jpg" alt="pic" />
再換成多線(xiàn)程的模式跑跑 ~ 個(gè)人不太喜歡用多線(xiàn)程,這東西屬于管你忙不忙,老大說(shuō)了要公平,我就算搶到了,但是沒(méi)事干,我也不讓給你。。。屬于那種蠻干的機(jī)制 ~
nima,要比單個(gè)跑的慢呀 ~ 線(xiàn)程這東西咋會(huì)這么不靠譜呀 ~
應(yīng)該是沒(méi)有做線(xiàn)程池 pool,拉取隊(duì)列。導(dǎo)致線(xiàn)程過(guò)多導(dǎo)致的。不然不可能比單進(jìn)程都要慢~
還有就是像這些涉及到 IO 的東西,交給協(xié)程的事件框架更加合理點(diǎn) !??!
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.utcnow()}
# goodinsert(post)
a=threading.Thread(target=goodinsert,args=(post,))
a.start()
http://wiki.jikexueyuan.com/project/python-actual-combat/images/23.jpg" alt="pic" />
python 畢竟有 gil 的限制,雖然 multiprocess 號(hào)稱(chēng)可以解決多進(jìn)程的。但是用過(guò)的朋友知道,這個(gè)東西更不靠譜 ~ 屬于坑人的東西 ~
要是有朋友懷疑是 python 的單進(jìn)程的性能問(wèn)題,那咱們就用 supervisord 跑了幾個(gè)后臺(tái)的 python 壓力腳本 ~ supervisord 的配置我就不說(shuō)了,我以前的文章里面有詳述的 ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/24.jpg" alt="pic" />
cpu 方面是跑的有點(diǎn)均勻了,但是 mongodb 那邊的壓力總是上不去
當(dāng)加大到 16 個(gè)后臺(tái)進(jìn)程做壓力測(cè)試的時(shí)候 ~ 大家會(huì)發(fā)現(xiàn) insert 很不穩(wěn)定。 看來(lái)他的極限也就是 2 MB 左右的數(shù)據(jù) ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/25.jpg" alt="pic" />
當(dāng)減少到 8 個(gè)壓力進(jìn)程的時(shí)候 ~ 我們發(fā)現(xiàn)他的 insert 慢慢的提供到正常了,也就是說(shuō) 他真的是 2 MB 的極限 ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/26.jpg" alt="pic" />
腳本里面是有做有序的 id 插入的,我們?cè)囋嚢?id 的插入給去掉,看看有沒(méi)有提升~
結(jié)果和不插入 id 差不多的結(jié)果 ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/27.jpg" alt="pic" />
調(diào)優(yōu)之后~ 再度測(cè)試
ulimit 的優(yōu)化
cat /etc/security/limits.conf
* soft nofile 102400
* hard nofile 102400
內(nèi)核的 tcp 優(yōu)化
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 = 8192 436600 873200
net.ipv4.tcp_rmem = 32768 436600 873200
net.ipv4.tcp_mem = 94500000 91500000 92700000
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_fin_timeout = 30
#直接生效
/sbin/sysctl -p
啟動(dòng)的時(shí)候,加上多核的優(yōu)化參數(shù)
多核問(wèn)題可以在啟動(dòng)時(shí)加入啟動(dòng)參數(shù): numactl --interleave=all
insert 的頻率已經(jīng)到了 2 w 左右 ~ 內(nèi)存占用了 8 G 左右 ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/28.png" alt="pic" />
http://wiki.jikexueyuan.com/project/python-actual-combat/images/288.png" alt="pic" />
我想到的一個(gè)方案:
當(dāng)然不用非要 celery,就算咱們用 socket 寫(xiě)分發(fā),和 zeromq 的 pub sub 也可以實(shí)現(xiàn)這些的。這是 celery 的調(diào)度更加專(zhuān)業(yè)點(diǎn)。
http://wiki.jikexueyuan.com/project/python-actual-combat/images/29.jpg" alt="pic" />
剛才我們測(cè)試的都是insert,現(xiàn)在我們?cè)賮?lái)測(cè)試下在千萬(wàn)級(jí)別數(shù)據(jù)量下的查詢(xún)?nèi)绾危?/p>
查詢(xún)正則的,以2開(kāi)頭的字符
posts = db.userinfo
for i in posts.find({"author":re.compile('^2.Mike')}):
print i
http://wiki.jikexueyuan.com/project/python-actual-combat/images/30.jpg" alt="pic" />
精確的查詢(xún):
查詢(xún)?cè)?5s 左右 ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/31.jpg" alt="pic" />
http://wiki.jikexueyuan.com/project/python-actual-combat/images/32.jpg" alt="pic" />
http://wiki.jikexueyuan.com/project/python-actual-combat/images/33.jpg" alt="pic" />
http://wiki.jikexueyuan.com/project/python-actual-combat/images/34.jpg" alt="pic" />
http://wiki.jikexueyuan.com/project/python-actual-combat/images/35.jpg" alt="pic" />
總結(jié):
典型的高讀低寫(xiě)數(shù)據(jù)庫(kù)!
本文出自 “峰云,就她了?!?博客,謝絕轉(zhuǎn)載!