從Redis的架構(gòu)看Redis使用優(yōu)化方面的幾個要點(diǎn)
最近的一些優(yōu)化和運(yùn)維項(xiàng)目中都有Redis,看樣子不論是互聯(lián)網(wǎng)架構(gòu)的應(yīng)用還是傳統(tǒng)架構(gòu)的應(yīng)用,都已經(jīng)意識到了訪問頻繁,數(shù)據(jù)結(jié)構(gòu)簡單的熱數(shù)據(jù)使用合理的訪問方式是十分重要的。既然客戶有需求,我們就需要去深入的研究一下怎么把Redis用好,優(yōu)化好。做一個運(yùn)維對象的分析其實(shí)也是有套路的,并不一定都是需要從十年八年的積累中才可以獲得,特別是針對Redis這樣比較簡單的內(nèi)存數(shù)據(jù)庫。
一般來說,對于這類相對簡單的運(yùn)維對象,我們在學(xué)習(xí)和梳理其要點(diǎn)的時候會首先從管理類、配置類、技術(shù)類三方面去了解它。把這些東西搞清楚了,這個運(yùn)維對象的一些基本的運(yùn)維,管理,優(yōu)化就差不多了。當(dāng)然要做這些事情之前的,一個十分重要的工作就是理解這個運(yùn)維對象的架構(gòu)。我覺得理解一個運(yùn)維對象的架構(gòu)對于今后去運(yùn)維管理,做優(yōu)化都是十分關(guān)鍵的。我和很多使用Redis開發(fā)應(yīng)用系統(tǒng)的人聊過,他們大多數(shù)都沒有關(guān)注過Redis的架構(gòu),反正給我變成接口,告訴我一些基本的操作,我就開干了,架構(gòu)啥的我不關(guān)注。事實(shí)上,一個想把Redis用好的程序員,也是需要去深入的理解Redis的架構(gòu)的。
Redis是一個輕量級的內(nèi)存緩沖組件,被廣泛的用作內(nèi)存數(shù)據(jù)庫、緩沖、消息代理、消息隊(duì)列等。Redis可以提供亞毫秒級的響應(yīng)時間,支持?jǐn)?shù)十萬甚至上百萬級別的并發(fā)訪問。不過很可能很多朋友都沒有關(guān)注到,Redis的核心從本質(zhì)上來說是單線程架構(gòu)的。
這是網(wǎng)上都可以找到的十分典型的Redis單實(shí)例架構(gòu)的邏輯架構(gòu)圖,是不是顯得太簡單了一點(diǎn),不過事實(shí)上Redis就是這樣的,十分簡單。實(shí)際上大多數(shù)內(nèi)存數(shù)據(jù)庫,哪怕是timesten這樣的內(nèi)存關(guān)系型數(shù)據(jù)庫,都會和普通的磁盤庫在體系架構(gòu)上有巨大的不同,這是因?yàn)閮?nèi)存與磁盤訪問在延時上有成千上萬倍的不同。Redis作為一種內(nèi)存KV數(shù)據(jù)庫,更需要十分簡單的方式來充分利用內(nèi)存的低延時特性,提供高吞吐量的訪問??赡苓€是有朋友無法理解為什么Redis設(shè)計(jì)之初不設(shè)計(jì)成多線程架構(gòu),讓Redis可以具有更高的吞吐能力。這個爭論早在5、6年前就有過了,最典型的是2014年在Quora上針對Redis架構(gòu)的爭論,我看過之后受益良多。其實(shí)在多線程架構(gòu)的數(shù)據(jù)庫中,鎖沖突是十分高開銷的爭用。相對于磁盤的IO延時來說,Enqueue的開銷可能還可以接受,而對于內(nèi)存的訪問速度來說,鎖爭用帶來的負(fù)面影響可能遠(yuǎn)超多線程帶來的好處。因此Redis在設(shè)計(jì)之初就選擇了無鎖的串行單線程訪問數(shù)據(jù)的架構(gòu)。甚至最初的Redis整體都是單線程架構(gòu)的。隨著Redis的發(fā)展,Redis也出現(xiàn)了一些多線程的特性,比如4.0開始,延遲大鍵的刪除操作,采用單獨(dú)的后臺進(jìn)程來處理,另外多線程也被用于一些較滿的IO操作。不管怎么發(fā)展Redis的核心數(shù)據(jù)訪問還是串行單線程,無鎖方式的訪問。這種單線程的架構(gòu)也讓應(yīng)用開發(fā)變得十分簡單,因?yàn)闊o需考慮鎖的問題,也不需要考慮回滾和提交。
這種單線程架構(gòu)決定了Redis是不怎么消耗CPU的,因此你無需為單個的Redis實(shí)例配置過多的CPU,一般來說,2-4顆邏輯CPU線程就完全足夠應(yīng)付任何場景的并發(fā)訪問了。
不過對于這種單線程架構(gòu),命令是串行執(zhí)行的,因此平均每條命令執(zhí)行的時間長度決定了單個Redis實(shí)例的并發(fā)訪問量,比如我們一條命令平均延時為20ns,那么一秒鐘有1000000ns,執(zhí)行命令的總數(shù)理論上限是1000000/20=5萬。比如下面的這個例子:
從報告上可以看出,平均每秒可以執(zhí)行2萬多條命令,而這些命令的執(zhí)行中位數(shù)是35ns,算起來20106*35大概是0.7秒左右。
從單線程架構(gòu)上我們也可以看出,Redis的并發(fā)訪問是需要串行排隊(duì)的,因此相同的命令,其執(zhí)行時間是不穩(wěn)定的,如果前面排隊(duì)的命令比較多,那么排在前面的這條命令的總體執(zhí)行時間比排在隊(duì)伍后面的快十倍也是很正常的。因此對于Redis應(yīng)用的性能分析,不能看單次的執(zhí)行時間,更重要的是要看平均時間,中位數(shù)時間,90分位時間等指標(biāo)。如果你的應(yīng)用的中位數(shù)執(zhí)行時間超過100ns,或者99分位數(shù)執(zhí)行時間超過2毫秒,那么你的應(yīng)用的性能是不能接受的,這會大大影響整個Redis實(shí)例上的應(yīng)用的性能。如果說普通的數(shù)據(jù)庫某條SQL慢點(diǎn)可能影響面有限,對于單線程的Redis來說,某些特別慢的命令是不能接受的,必須進(jìn)行優(yōu)化或者進(jìn)行隔離,否則一顆老鼠屎可能會壞了一鍋湯。
從Redis的單線程架構(gòu),也給我們的應(yīng)用的橫向擴(kuò)展能力提出了要求。剛才我們也計(jì)算過了,單一的Redis實(shí)例的最大并發(fā)量是有限的,我們能夠?qū)?yīng)用做的優(yōu)化也是有極限的。因此使用Redis的應(yīng)用,如果需要支撐較大的并發(fā)量的話,一定要能夠很方便的橫向擴(kuò)展的。我們可以通過Redis Cluster來做分片處理,通過多個Redis的集群來成倍的擴(kuò)充Redis服務(wù)的并發(fā)量。
從Redis的單線程架構(gòu)上來看,Redis數(shù)據(jù)庫是內(nèi)存敏感的,我們一定要確保Redis服務(wù)器的操作系統(tǒng)內(nèi)存的充足,Redis也提供了大了的監(jiān)控信息來幫我們分析內(nèi)存是否足夠。當(dāng)服務(wù)器內(nèi)存不足的時候,OOM KILLER要?dú)⒌目隙ㄊ荝edis服務(wù),因此我們也要確保Redis服務(wù)不會成為首先被殺的對象。
mem_fragmentation_ratio是一個十分值得關(guān)注的指標(biāo),這個指標(biāo)出現(xiàn)異常,會引發(fā)REDIS的性能問題。如果這個指標(biāo)超過1.5,說明Redis數(shù)據(jù)庫存在較大的碎片,碎片會引起內(nèi)存訪問性能問題,從而影響數(shù)據(jù)庫的總體性能。而如果這個指標(biāo)小于1,說明數(shù)據(jù)庫中有一部分內(nèi)存被放入swap了,這更會引發(fā)更大的Redis性能問題。我們這臺服務(wù)器上除了跑Redis外還有我們的一些其他的應(yīng)用,包括postresql數(shù)據(jù)庫、tomcat服務(wù)器等,最近總會出現(xiàn)內(nèi)存不足的情況,swap使用率經(jīng)常超過50%??梢钥闯觯承r段里,Redis出現(xiàn)了mem_fragmentation_ratio小于1的情況。如果你們的生產(chǎn)系統(tǒng)出現(xiàn)這種情況,那么給服務(wù)器或者虛擬機(jī)擴(kuò)內(nèi)存是十分必要的。
另外一點(diǎn),從Redis是單線程的內(nèi)核態(tài)訪問為主的應(yīng)用,那么其CPU資源消耗上,應(yīng)該大部分的CPU都是可心態(tài)的訪問,因此對于一臺只是跑Redis數(shù)據(jù)庫的服務(wù)器來說,sys的cpu比例應(yīng)該很高。
在這個監(jiān)控指標(biāo)中,我們看出sys和user差不多,這是因?yàn)槲覀兊姆?wù)器上還有PG數(shù)據(jù)庫的原因。如果我們在自己的Redis服務(wù)器上發(fā)現(xiàn)了這種現(xiàn)象,那么就需要分析一下到底哪些非Redis實(shí)例在消耗CPU資源了。
原本今天早上準(zhǔn)備用半小時寫篇小文,于是考慮寫寫比較簡單的Redis,沒想打一下子就到9點(diǎn)了,馬上有很多事要做,先到此打住吧。哪怕是這么簡單的單線程的Redis,寫了半天好像剛剛開了個頭。IT基礎(chǔ)設(shè)施的運(yùn)維確實(shí)還是挺費(fèi)勁的。
本文轉(zhuǎn)載自微信公眾號「白鱔的洞穴」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系
公眾號。