為什么說很多NoSQL的Benchmark是扯淡?
抱歉我用了這么一個標題黨的題目做為標題。
寫這篇文章只是想引起大家的注意:在選擇NoSQL產(chǎn)品時,達到標稱性能,需要諸多限制條件,例如本文主要討論的磁盤I/O。
現(xiàn)在NoSQL的產(chǎn)品已經(jīng)很多了,很多都宣稱“我們的QPS可以達到十萬,甚至百萬”,但是當我們在生產(chǎn)環(huán)境中使用的時候,卻明顯的感覺到,隨著數(shù)據(jù)文件不斷增大,NoSQL的性能卻指數(shù)下降,問題處在哪里了?
這些NOSQL的Benchmark的量都有一個前提“你得內(nèi)存足夠放下你的全部數(shù)據(jù)文件”
Case1:有人說,我內(nèi)存16GB,那只能說明你得數(shù)據(jù)規(guī)模還不夠大……我已經(jīng)經(jīng)歷被無數(shù)上百GB的數(shù)據(jù)庫折磨過了。此外,你可能需要在1GB內(nèi)存的虛擬機上支撐數(shù)10GB的數(shù)據(jù),比如我現(xiàn)在的情況。
繼續(xù)討論,一旦內(nèi)存放不下全部的數(shù)據(jù),會怎么辦呢?
有很多策略,但無非都是訪問磁盤,將數(shù)據(jù)Cache到內(nèi)存中。
我們先討論最壞的情況,假定每條記錄的偏移是放在內(nèi)存中,但所有數(shù)據(jù)都放在磁盤,我們使用fseek等操作來查詢磁盤。
來看下面的測試代碼。
- void test_fseek_set()
- {
- long offset;
- FILE*fp = NULL;
- long i;
- fp = fopen(FILE_NAME, "r");
- if(!fp)
- {
- printf("Open file fail.\n");
- printf("%s\n",strerror(errno));
- exit(-1);
- }
- for(i=0; i<TIMES; i++)
- {
- //Because random max is 1<<30 - 1
- offset = random() * 10 % MAX_FILE;
- if(fseek(fp, offset, SEEK_SET))
- {
- printf("fseek error.\n");
- printf("%ld",offset);
- printf("%s\n",strerror(errno));
- }
- }
- fclose(fp);
- }
好了,你猜猜上述隨機fseek的程序在一個7200轉(zhuǎn)的硬盤上,針對一個4GB的文件隨機訪問,能跑多塊?
答案是QPS<=80。
有人說你騙人,我跑的能到1XXX,那么請你執(zhí)行下述命令清空你內(nèi)存中的磁盤緩存。
- sync; echo 3 > /proc/sys/vm/drop_caches
很多時候,之所以我們能在小數(shù)據(jù)時達到NoSQL官方標稱的QPS,而大數(shù)據(jù)量卻指數(shù)下降,都是這些緩存在作怪。說白了,我們很Happy的Benchmark半天,實際是在玩系統(tǒng)的緩存,當然快了。
一旦你的數(shù)據(jù)文件大于內(nèi)存磁盤緩存,那么速度會馬上像我列舉的這樣,不會多余80QPS,在一個4GB的文件上。
有人說mmap,我曾經(jīng)也是這樣YY的,但根據(jù)我的測試,事情不是這樣。
我有一個120GB的Tokyo Cabinet數(shù)據(jù)文件,把內(nèi)存開滿,它默認會用mmap,然后你會發(fā)現(xiàn)top中“VIRT”一列,會顯示為120GB+(換算后),而我得機器內(nèi)存卻只 有32GB。這時,當你訪問恰好不在內(nèi)存中的那部分數(shù)據(jù)時,操作系統(tǒng)會進行非常耗時的換入換出操作(首先就需要fseek等)。在這臺24核、32GB的 機器上,QPS勉強能達到3000(這已經(jīng)遠遠低于標稱的QPS),而一旦清空緩存,QPS會迅速跌落到70左右……
可能還會有人說:我沒事閑的為啥要自己清空緩存?
機器不是給你一個NoSQL進程服務的,很多系統(tǒng)其他服務都需要訪問磁盤,讀取文件,漸漸的就會把你Cache起來的內(nèi)存全部換掉,根據(jù)實際測試的 情況,一臺完全閑置的機器,開TT能達到3000, 閑置放置48小時(不開其他服務), 性能就會驟降到1000左右,再放置72小時左右,就回歸到70的qps了,此時Cache已經(jīng)基本完全換出。
綜上,mmap不是神,因為你的內(nèi)存不夠,而其他進程也會爭奪內(nèi)存來做自己的Cache。
如果你想充分發(fā)揮NoSQL的性能,建議用支持集群的NoSQL產(chǎn)品,盡量將全部數(shù)據(jù)放入內(nèi)存中。
或者你沒錢購置很多Moster內(nèi)存的服務器,像我一樣,就不要期望NoSQL能有很驚人的性能了。此時,NoSQL所能帶來的提升,只是關(guān)系數(shù)據(jù)庫所剪掉的那部分開銷,如果你基本沒有什么join,那么可能還會不如關(guān)系數(shù)據(jù)庫。
分析性能,我們不能僅僅看官方的數(shù)據(jù)比較,要考慮機器的實際情況和自己的數(shù)據(jù)規(guī)模,最終才能分析出瓶頸出在哪里。
對于原作者的觀點,本人提出兩點看法:
- 要充分發(fā)揮NoSQL性能,并不是一定要盡量把所有數(shù)據(jù)放到內(nèi)存,實際上只要保證了熱數(shù)據(jù)都能裝在內(nèi)存中就夠了。
- 作者舉例中的程序,主要用了磁盤seek,磁盤的seek速度慢,原本就是磁盤物理結(jié)構(gòu)的硬傷,所以許多NoSQL存儲采用了變隨機寫為順序?qū)懙姆绞?,減少磁盤seek操作,也是提升IO性能的良方。
【編輯推薦】