鍍金池/ 教程/ Python/ Mongodb 千萬級數(shù)據(jù)在 python 下的綜合壓力測試及應(yīng)用探討
通過 memcached 實(shí)現(xiàn)領(lǐng)號排隊(duì)功能及 python 隊(duì)列實(shí)例
利用 pypy 提高 python 腳本的執(zhí)行速度及測試性能
Python FAQ3-python 中 的原始(raw)字符串
Mongodb 千萬級數(shù)據(jù)在 python 下的綜合壓力測試及應(yīng)用探討
Parallel Python 實(shí)現(xiàn)程序的并行多 cpu 多核利用【pp 模塊】
python simplejson 模塊淺談
服務(wù)端 socket 開發(fā)之多線程和 gevent 框架并發(fā)測試[python 語言]
python Howto 之 logging 模塊
python 之 MySQLdb 庫的使用
關(guān)于 python 調(diào)用 zabbix api 接口的自動化實(shí)例 [結(jié)合 saltstack]
python 之利用 PIL 庫實(shí)現(xiàn)頁面的圖片驗(yàn)證碼及縮略圖
Python 通過 amqp 消息隊(duì)列協(xié)議中的 Qpid 實(shí)現(xiàn)數(shù)據(jù)通信
python 中用 string.maketrans 和 translate 巧妙替換字符串
python linecache 模塊讀取文件用法詳解
Python 批量更新 nginx 配置文件
python 計(jì)算文件的行數(shù)和讀取某一行內(nèi)容的實(shí)現(xiàn)方法
python+Django 實(shí)現(xiàn) Nagios 自動化添加監(jiān)控項(xiàng)目
多套方案來提高 python web 框架的并發(fā)處理能力
python 寫報(bào)警程序中的聲音實(shí)現(xiàn) winsound
python 調(diào)用 zabbix 的 api 接口添加主機(jī)、查詢組、主機(jī)、模板
對 Python-memcache 分布式散列和調(diào)用的實(shí)現(xiàn)
使用 python 構(gòu)建基于 hadoop 的 mapreduce 日志分析平臺
一個(gè)腳本講述 python 語言的基礎(chǔ)規(guī)范,適合初學(xué)者
Python 編寫的 socket 服務(wù)器和客戶端
如何將 Mac OS X10.9 下的 Python2.7 升級到最新的 Python3.3
python 監(jiān)控文件或目錄變化
報(bào)警監(jiān)控平臺擴(kuò)展功能 url 回調(diào)的設(shè)計(jì)及應(yīng)用 [python 語言]
Python 處理 cassandra 升級后的回滾腳本
python 實(shí)現(xiàn) select 和 epoll 模型 socket 網(wǎng)絡(luò)編程
關(guān)于 B+tree (附 python 模擬代碼)
通過 python 和 websocket 構(gòu)建實(shí)時(shí)通信系統(tǒng)[擴(kuò)展 saltstack 監(jiān)控]

Mongodb 千萬級數(shù)據(jù)在 python 下的綜合壓力測試及應(yīng)用探討

曾經(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)載!