曾經(jīng)在收集數(shù)據(jù)的項(xiàng)目中,用過 mongodb 的數(shù)據(jù)存儲,但是當(dāng)數(shù)據(jù)很大的時(shí)候,還是比較的吃力。很可能當(dāng)時(shí)的應(yīng)用水平不高,也可以是當(dāng)時(shí)的服務(wù)器不是很強(qiáng)。 所以這次能力比以前高點(diǎn)了,然后服務(wù)器比以前也高端了很多,好嘞 ~再測試下。
(更多的是單機(jī)測試,沒有用復(fù)制分片的測試 ~)!
相比較 MySQL,MongoDB 數(shù)據(jù)庫更適合那些讀作業(yè)較重的任務(wù)模型。MongoDB 能充分利用機(jī)器的內(nèi)存資源。如果機(jī)器的內(nèi)存資源豐富的話,MongoDB 的查詢效率會快很多。
這次測試的服務(wù)器是 dell 的 r510!
http://wiki.jikexueyuan.com/project/python-actual-combat/images/12.jpg" alt="pic" />
內(nèi)存還行,是 48 G 的,本來想讓同事給加滿,但是最終還是沒有說出口 ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/13.jpg" alt="pic" />
磁盤是 10 個(gè) 2T 的,但是因?yàn)楦袷交臅r(shí)間太久了,哥們直接把其他的硬盤給拔出來了,就用了三個(gè)盤。。。data 目錄沒有做 raid,是為了讓他們體現(xiàn)更好的硬盤速度。
http://wiki.jikexueyuan.com/project/python-actual-combat/images/14.jpg" alt="pic" />
既然說好了是在 python 下的應(yīng)用測試,那就需要安裝 mongodb python 下的模塊! 對了,不知道 mongodb-server 的安裝要不要說下?
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ù)庫
db.authenticate('username', 'password') # 登錄
db.drop_collection('users') #刪除表
db.logout() # 退出
db.collection_names() # 查看所有表
db.users.count() # 查詢數(shù)量
db.users.find_one({'name' : 'xiaoming'}) # 單個(gè)對象
db.users.find({'age' : 18}) # 所有對象
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) # 切片
測試的代碼:
#!/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))
咱們就先來個(gè)百萬的數(shù)據(jù)做做測試~
綜合點(diǎn)的數(shù)據(jù):
http://wiki.jikexueyuan.com/project/python-actual-combat/images/16.jpg" alt="pic" />
在 top 下看到的程序占用資源的情況 ~ 我們看到的是有兩個(gè)進(jìn)程的很突出,對頭 ! 正是 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 每秒查詢次數(shù)
update/s 每秒更新次數(shù)
delete/s 每秒刪除次數(shù)
getmore/s 每秒執(zhí)行 getmore 次數(shù)
command/s 每秒的命令數(shù),比以上插入、查找、更新、刪除的綜合還多,還統(tǒng)計(jì)了別的命令
flushs/s 每秒執(zhí)行 fsync 將數(shù)據(jù)寫入硬盤的次數(shù)。
mapped/s 所有的被 mmap 的數(shù)據(jù)量,單位是 MB,
vsize 虛擬內(nèi)存使用量,單位 MB
res 物理內(nèi)存使用量,單位 MB
faults/s 每秒訪問失敗數(shù)(只有 Linux 有),數(shù)據(jù)被交換出物理內(nèi)存,放到 swap。不要超過 100,否則就是機(jī)器內(nèi)存太小,造成頻繁 swap 寫入。此時(shí)要升級內(nèi)存或者擴(kuò)展
locked % 被鎖的時(shí)間百分比,盡量控制在 50% 以下吧
idx miss % 索引不命中所占百分比。如果太高的話就要考慮索引是不是少了
q t|r|w 當(dāng) Mongodb 接收到太多的命令而數(shù)據(jù)庫被鎖住無法執(zhí)行完成,它會將命令加入隊(duì)列。這一欄顯示了總共、讀、寫 3 個(gè)隊(duì)列的長度,都為 0 的話表示 mongo 毫無壓力。高并發(fā)時(shí),一般隊(duì)列值會升高。
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" />
然后我們在測試下在一千萬的數(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" />
再換成多線程的模式跑跑 ~ 個(gè)人不太喜歡用多線程,這東西屬于管你忙不忙,老大說了要公平,我就算搶到了,但是沒事干,我也不讓給你。。。屬于那種蠻干的機(jī)制 ~
nima,要比單個(gè)跑的慢呀 ~ 線程這東西咋會這么不靠譜呀 ~
應(yīng)該是沒有做線程池 pool,拉取隊(duì)列。導(dǎo)致線程過多導(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 號稱可以解決多進(jìn)程的。但是用過的朋友知道,這個(gè)東西更不靠譜 ~ 屬于坑人的東西 ~
要是有朋友懷疑是 python 的單進(jìn)程的性能問題,那咱們就用 supervisord 跑了幾個(gè)后臺的 python 壓力腳本 ~ supervisord 的配置我就不說了,我以前的文章里面有詳述的 ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/24.jpg" alt="pic" />
cpu 方面是跑的有點(diǎn)均勻了,但是 mongodb 那邊的壓力總是上不去
當(dāng)加大到 16 個(gè)后臺進(jìn)程做壓力測試的時(shí)候 ~ 大家會發(fā)現(xiàn) insert 很不穩(wěn)定。 看來他的極限也就是 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 慢慢的提供到正常了,也就是說 他真的是 2 MB 的極限 ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/26.jpg" alt="pic" />
腳本里面是有做有序的 id 插入的,我們試試把 id 的插入給去掉,看看有沒有提升~
結(jié)果和不插入 id 差不多的結(jié)果 ~
http://wiki.jikexueyuan.com/project/python-actual-combat/images/27.jpg" alt="pic" />
調(diào)優(yōu)之后~ 再度測試
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
啟動的時(shí)候,加上多核的優(yōu)化參數(shù)
多核問題可以在啟動時(shí)加入啟動參數(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 寫分發(fā),和 zeromq 的 pub sub 也可以實(shí)現(xiàn)這些的。這是 celery 的調(diào)度更加專業(yè)點(diǎn)。
http://wiki.jikexueyuan.com/project/python-actual-combat/images/29.jpg" alt="pic" />
剛才我們測試的都是insert,現(xiàn)在我們再來測試下在千萬級別數(shù)據(jù)量下的查詢?nèi)绾危?/p>
查詢正則的,以2開頭的字符
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" />
精確的查詢:
查詢在 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é):
典型的高讀低寫數(shù)據(jù)庫!
本文出自 “峰云,就她了?!?博客,謝絕轉(zhuǎn)載!