從我們的直觀感受來(lái)講间螟,對(duì)于任何服務(wù),只要在中間增加了一層损肛,肯定會(huì)對(duì)服務(wù)性能造成影響厢破。那么到底會(huì)影響什么呢?在考察一個(gè)服務(wù)性能的時(shí)候治拿,有兩個(gè)最重要的指標(biāo)摩泪,那就是吞吐和延遲。吞吐定義為服務(wù)端單位時(shí)間內(nèi)能處理的請(qǐng)求數(shù)劫谅,延遲定義為客戶端從發(fā)出請(qǐng)求到收到請(qǐng)求的耗時(shí)见坑。中間環(huán)節(jié)的引入我們首先想到的就是那會(huì)增加處理時(shí)間,這就會(huì)增加服務(wù)的延遲捏检,于是順便我們也會(huì)認(rèn)為吞吐也會(huì)下降鳄梅。從單個(gè)用戶的角度來(lái)講,事實(shí)確實(shí)如此未檩,我完成一個(gè)請(qǐng)求的時(shí)間增加了戴尸,那么我單位時(shí)間內(nèi)所能完成的請(qǐng)求量必定就減少了。然而站在服務(wù)端的角度來(lái)看冤狡,雖然單個(gè)請(qǐng)求的處理時(shí)間增加了孙蒙,但是總的吞吐就一定會(huì)減少嗎项棠?
接下來(lái)我們就來(lái)對(duì)redis來(lái)進(jìn)行一系列的測(cè)試,利用redis自帶的redis-benchmark挎峦,分別對(duì)set和get命令香追;單個(gè)發(fā)送和批量發(fā)送;直連redis和連接redis代理predixy坦胶。這樣組合起來(lái)總共就是八種情況透典。redis-benchmark、redis是單線程的顿苇,predixy支持多線程峭咒,但是我們也只運(yùn)行一個(gè)線程,這三個(gè)程序都運(yùn)行在一臺(tái)機(jī)器上纪岁。
項(xiàng)目 | 內(nèi)容 |
---|---|
CPU | AMD Ryzen 7 1700X Eight-Core Processor 3.775GHz |
內(nèi)存 | 16GB DDR4 3000 |
OS | x86_64 GNU/Linux 4.10.0-42-generic #46~16.04.1-Ubuntu |
redis | 版本3.2.9凑队,端口7200 |
predixy | 版本1.0.2,端口7600 |
八個(gè)測(cè)試命令
測(cè)試命令 | 命令行 |
---|---|
redis set | redis-benchmark -h xxx -p 7200 -t set -r 3000 -n 40000000 |
predixy set | redis-benchmark -h xxx -p 7600 -t set -r 3000 -n 40000000 |
redis get | redis-benchmark -h xxx -p 7200 -t get -r 3000 -n 40000000 |
predixy get | redis-benchmark -h xxx -p 7600 -t get -r 3000 -n 40000000 |
redis 批量set | redis-benchmark -h xxx -p 7200 -t set -r 3000 -n 180000000 -P 20 |
predixy 批量set | redis-benchmark -h xxx -p 7600 -t set -r 3000 -n 180000000 -P 20 |
redis 批量get | redis-benchmark -h xxx -p 7200 -t get -r 3000 -n 420000000 -P 20 |
predixy 批量get | redis-benchmark -h xxx -p 7600 -t get -r 3000 -n 220000000 -P 20 |
以上8條命令采取redis-benchmark默認(rèn)的50個(gè)并發(fā)連接幔翰,數(shù)據(jù)大小為2字節(jié)漩氨,指定3000個(gè)key,批量測(cè)試時(shí)一次發(fā)送20個(gè)請(qǐng)求遗增。依次間隔2分鐘執(zhí)行以上命令叫惊,每一個(gè)測(cè)試完成時(shí)間大約4分鐘。最后得到下圖的總體結(jié)果
眼花繚亂是不是做修?左邊的縱軸表示CPU使用率赋访,右邊的縱軸表示吞吐。其中redis used表示redis總的CPU使用率缓待,redis user表示redis CPU用戶態(tài)使用率蚓耽,redis sys表示redis CPU內(nèi)核態(tài)使用率,其它類(lèi)推旋炒。先別擔(dān)心分不清里面的內(nèi)容步悠,下面我們會(huì)一一標(biāo)出數(shù)值來(lái)。在這圖中總共可以看出有八個(gè)凸起瘫镇,依次對(duì)應(yīng)我們上面提到的八個(gè)測(cè)試命令鼎兽。
1、 redis set測(cè)試
2铣除、 predixy set測(cè)試
3谚咬、 redis get測(cè)試
4、 predixy get測(cè)試
5尚粘、 redis批量set測(cè)試
6择卦、 predixy批量set測(cè)試
7、 redis批量get測(cè)試
8、 predixy批量get測(cè)試
填成表格
測(cè)試-指標(biāo) | redis used | redis user | redis sys | predixy used | predixy user | predixy sys | redis qps | predixy qps |
---|---|---|---|---|---|---|---|---|
redis set | 0.990 | 0.247 | 0.744 | 0 | 0 | 0 | 167000 | 3 |
predixy set | 0.475 | 0.313 | 0.162 | 0.986 | 0.252 | 0.734 | 174000 | 174000 |
redis get | 0.922 | 0.180 | 0.742 | 0 | 0 | 0 | 163000 | 3 |
predixy get | 0.298 | 0.195 | 0.104 | 0.988 | 0.247 | 0.741 | 172000 | 172000 |
redis批量set | 1.006 | 0.796 | 0.21 | 0 | 0 | 0 | 782000 | 3 |
predixy批量set | 0.998 | 0.940 | 0.058 | 0.796 | 0.539 | 0.256 | 724000 | 724000 |
redis批量get | 1 | 0.688 | 0.312 | 0 | 0 | 0 | 1708000 | 3 |
predixy批量get | 0.596 | 0.582 | 0.014 | 0.999 | 0.637 | 0.362 | 935000 | 935000 |
看到前四個(gè)的結(jié)果如果感到驚訝不用懷疑是自己看錯(cuò)了或者是測(cè)試結(jié)果有問(wèn)題秉继,這個(gè)結(jié)果是無(wú)誤的祈噪。根據(jù)這個(gè)結(jié)果,那么可以回答我們最初提出的疑問(wèn)尚辑,增加了代理之后并不一定會(huì)降低服務(wù)整體的吞吐辑鲤!雖然benchmark并不是我們的實(shí)際應(yīng)用,但是redis的大部分應(yīng)用場(chǎng)景都是這樣的杠茬,并發(fā)的接受眾多客戶端的請(qǐng)求月褥,處理然后返回。
為什么會(huì)是這樣的結(jié)果瓢喉,看到這個(gè)結(jié)果后我們肯定想知道原因宁赤,這好像跟我們的想象不太一樣。要分析這個(gè)問(wèn)題灯荧,我們還是從測(cè)試的數(shù)據(jù)來(lái)入手,首先看測(cè)試1的數(shù)據(jù)盐杂,redis的CPU使用率幾乎已經(jīng)達(dá)到了1逗载,對(duì)于單線程程序來(lái)說(shuō),這意味著CPU已經(jīng)跑滿了链烈,性能已經(jīng)達(dá)到了極限厉斟,不可能再提高了,然而這時(shí)redis的吞吐卻只有167000强衡。測(cè)試2的redis吞吐都比它高擦秽,并且我們明顯能看出測(cè)試2里redis的CPU使用率還不如測(cè)試1的高,測(cè)試2里redis CPU使用率只有0.475漩勤。為什么CPU使用率降低了吞吐反而卻還高了呢感挥?仔細(xì)對(duì)比一下兩個(gè)測(cè)試的數(shù)據(jù),可以發(fā)現(xiàn)在測(cè)試1里越败,redis的CPU大部分都花在了內(nèi)核態(tài)触幼,高達(dá)0.744,而用戶態(tài)只有0.247究飞,CPU運(yùn)行在內(nèi)核態(tài)時(shí)雖然我們不能稱之為瞎忙活置谦,但是卻無(wú)助于提升程序的性能,只有CPU運(yùn)行在用戶態(tài)才可能提升我們的程序性能亿傅,相比測(cè)試1媒峡,測(cè)試2的redis用戶態(tài)CPU使用率提高到了0.313,而內(nèi)核態(tài)CPU則大幅下降至0.162葵擎。這也就解釋了為什么測(cè)試2的吞吐比測(cè)試1還要高谅阿。當(dāng)然了,我們還是要繼續(xù)刨根問(wèn)底,為什么測(cè)試2里經(jīng)過(guò)一層代理predixy后奔穿,redis的CPU使用情況發(fā)生變化了呢镜沽?這是因?yàn)閞edis接受一個(gè)連接批量的發(fā)送命令過(guò)來(lái)處理,也就是redis里所謂的pipeline贱田。而predixy正是利用這一特性缅茉,predixy與redis之間只有一個(gè)連接(大多數(shù)情況下),predixy在收到客戶端的請(qǐng)求后男摧,會(huì)將它們批量的通過(guò)這個(gè)連接發(fā)送給redis處理蔬墩,這樣一來(lái)就大大降低了redis用于網(wǎng)絡(luò)IO操作的開(kāi)銷(xiāo),而這一部分開(kāi)銷(xiāo)基本都是花費(fèi)在內(nèi)核態(tài)耗拓。
對(duì)比測(cè)試1和測(cè)試2拇颅,引入predixy不僅直接提高了吞吐,還帶來(lái)一個(gè)好處乔询,就是redis的CPU使用率只有一半不到了樟插,這也就意味著如果我再把剩下的這一半CPU用起來(lái)還可以得到更高的吞吐,而如果沒(méi)有predixy這樣一層的話竿刁,測(cè)試1結(jié)果告訴我們r(jià)edis的CPU利用率已經(jīng)到頭了黄锤,吞吐已經(jīng)不可能再提高。
測(cè)試3和測(cè)試4說(shuō)明的問(wèn)題與測(cè)試1和測(cè)試2一樣食拜,如果我只做了這四個(gè)測(cè)試鸵熟,那么看起來(lái)好像代理的引入完全有助于提升我們的吞吐嘛。正如上面所分析的那樣负甸,predixy提升吞吐的原因是因?yàn)椴捎昧伺堪l(fā)送手段流强。那么如果客戶端的使用場(chǎng)景就是批量發(fā)送命令,那結(jié)果會(huì)如何呢呻待?
于是有了后面四個(gè)測(cè)試打月,后面四個(gè)測(cè)試給我們的直接感受就是太震撼了,吞吐直接提升幾倍甚至10倍蚕捉!其實(shí)也正是因?yàn)閞edis批量模式下性能非常強(qiáng)悍僵控,才使得predixy在單命令情況下改進(jìn)吞吐成為可能。當(dāng)然到了批量模式鱼冀,從測(cè)試結(jié)果看报破,predixy使得服務(wù)的吞吐下降了。
具體到批量set時(shí)千绪,直連redis和通過(guò)predixy充易,redis的CPU使用率都滿了,雖然采用predixy使得redis的用戶態(tài)CPU從0.796提高到了0.940荸型,但是吞吐卻不升反降盹靴,從782000到724000,大約下降了7.4%。至于為什么用戶態(tài)CPU利用率提高了吞吐卻下降了稿静,要想知道原因就需要分析redis本身的實(shí)現(xiàn)梭冠,這里我們就不做詳細(xì)探討「谋福可以做一個(gè)粗糙的解釋?zhuān)趓edis CPU跑滿的情況下控漠,不同的負(fù)載情況會(huì)使得用戶態(tài)和內(nèi)核態(tài)的使用率不同,而這其中有一種分配情況會(huì)是吞吐最大悬钳,而用戶態(tài)使用率高于或者低于這種情況時(shí)都會(huì)出現(xiàn)吞吐下降的情況盐捷。
再來(lái)看批量get,直連redis時(shí)吞吐高達(dá)1708000默勾,而通過(guò)predixy的話就只有935000了碉渡,下降了45%!就跟納了個(gè)人所得稅上限一般母剥≈团担看到這,剛剛對(duì)predixy建立的美好形象是不是又突然覺(jué)得要坍塌了环疼?先別急习霹,再看看其它指標(biāo),直連redis時(shí)秦爆,redis CPU跑滿序愚;而通過(guò)predixy時(shí)redis CPU只用了0.596憔披,也就是說(shuō)redis還有四成的CPU等待我們?nèi)赫ァ?br>
寫(xiě)到這等限,既然上面提到批量get時(shí),通過(guò)predixy的話redis并未發(fā)揮出全部功力芬膝,于是就想著如果全部發(fā)揮出來(lái)會(huì)是什么情況呢望门?我們繼續(xù)增加兩個(gè)測(cè)試,既然單個(gè)predixy在批量的情況下造成了吞吐下降锰霜,但是給我們帶來(lái)了一個(gè)好處是redis還可以提升的余地筹误,那么我們就增加predixy的處理能力。因此我們把predixy改為三線程癣缅,再來(lái)跑一遍測(cè)試6和測(cè)試8厨剪。
兩個(gè)測(cè)試的整體結(jié)果如下。
三線程predixy批量set
三線程predixy批量get
測(cè)試\指標(biāo) | redis used | redis user | redis sys | predixy used | predixy user | predixy sys | redis qps | predixy qps |
---|---|---|---|---|---|---|---|---|
predixy pipeline set | 1.01 | 0.93 | 0.07 | 1.37 | 0.97 | 0.41 | 762000 | 762000 |
predixy pipeline get | 0.93 | 0.85 | 0.08 | 2.57 | 1.85 | 0.72 | 1718000 | 1718000 |
原本在單線程predixy的批量set測(cè)試中友存,predixy和redis的CPU都已經(jīng)跑滿了祷膳,我們覺(jué)得吞吐已經(jīng)達(dá)到了極限,但是實(shí)際結(jié)果顯示在三線程predixy的批量set測(cè)試中屡立,吞吐還是提高了直晨,從原來(lái)的724000到現(xiàn)在的76200,與直連的782000只有2.5%的差距。多線程和單線程的主要差別在于單線程時(shí)predixy與redis只有一個(gè)連接勇皇,而三線程時(shí)有三個(gè)連接罩句。
而對(duì)于三線程predixy的批量get測(cè)試,不出我們所料的吞吐得到了極大的提升敛摘,從之前的935000直接飆到1718000门烂,已經(jīng)超過(guò)了直連的1708000。
最后着撩,我們來(lái)總結(jié)一下诅福,我們整個(gè)測(cè)試的場(chǎng)景比較簡(jiǎn)單,只是單純的set拖叙、get測(cè)試氓润,并且數(shù)據(jù)大小為默認(rèn)的2字節(jié),實(shí)際的redis應(yīng)用場(chǎng)景遠(yuǎn)比這復(fù)雜的多薯鳍。但是測(cè)試結(jié)果的數(shù)據(jù)依舊可以給我們一些結(jié)論咖气。代理的引入并不一定會(huì)降低服務(wù)的吞吐,實(shí)際上根據(jù)服務(wù)的負(fù)載情況挖滤,有時(shí)候引入代理反而可以提升整個(gè)服務(wù)的吞吐崩溪,如果我們不計(jì)較代理本身所消耗的資源,那么引入代理幾乎總是一個(gè)好的選擇斩松。根據(jù)我們上面的分析伶唯,一個(gè)最簡(jiǎn)單實(shí)用的判斷原則,看看你的redis CPU使用情況惧盹,如果花費(fèi)了太多時(shí)間在內(nèi)核態(tài)乳幸,那么考慮引入代理吧。