Redis是典型的單線程架構寇壳,所有的讀寫操作都是在一條主線程中完成的添坊。當Redis用于高并發(fā)場景時太伊,這條線程就變成了它的生命線雇锡。如果出現(xiàn)阻塞,哪怕是很短時間僚焦,對于應用來說都是噩夢锰提。
導致阻塞問題的原因:
- 內在原因:不合理地使用API或數(shù)據(jù)結構、CPU飽和芳悲、持久化阻塞等
- 外在原因:CPU競爭立肘、內存交換、網(wǎng)絡問題等
一名扛、發(fā)現(xiàn)阻塞
- 應用方加入異常監(jiān)控谅年,如日志系統(tǒng),比如Java語言中的logback或log4j
- Redis監(jiān)控系統(tǒng)肮韧,如CacheCloud
二融蹂、內在原因
2.1 API或數(shù)據(jù)結構使用不合理
通常Redis執(zhí)行命令速度非常快弄企,但是超燃,如果對一個包含上萬個元素的hash結構執(zhí)行hgetall操作,由于數(shù)據(jù)量比較大且命令算法復雜度是O(n)桩蓉,這條命令執(zhí)行速度必然很慢淋纲。
對于高并發(fā)的場景應該盡量避免在大對象上執(zhí)行算法復雜度超過O(n)的命令。
(1)如何發(fā)現(xiàn)慢查詢
Redis原生提供慢查詢統(tǒng)計功能院究,執(zhí)行slowlog get{n}命令可以獲取最近的n條慢查詢命令洽瞬,默認對于執(zhí)行超過10毫秒的命令都會記錄到一個定長隊列中,線上實例建議設置為1毫秒便于及時發(fā)現(xiàn)毫秒級以上的命令业汰。
(2)發(fā)現(xiàn)慢查詢后如何調整
- 修改為低算法復雜度的命令
- 調整大對象:縮減大對象數(shù)據(jù)或把大對象拆分為多個小對象伙窃,防止一次命令操作過多的數(shù)據(jù)。大對象拆分過程需要視具體的業(yè)務決定样漆,如用戶好友集合存儲在Redis中为障,有些熱點用戶會關注大量好友,這時可以按時間或其他維度拆分到多個集合中。
(3)如何發(fā)現(xiàn)大對象
Redis本身提供發(fā)現(xiàn)大對象的工具鳍怨。具體命令:
redis-cli -h {ip} -p {port} --bigkeys
內部原理采用分段進行scan操作呻右,把歷史掃描過的最大對象統(tǒng)計出來便于分析優(yōu)化。
2.2 CPU飽和
單線程的Redis處理命令時只能使用一個CPU鞋喇。而CPU飽和是指Redis把單核CPU使用率跑到接近100%声滥。使用top命令很容易識別出對應Redis進程的CPU使用率。CPU飽和是非常危險的侦香,將導致Redis無法處理更多的命令落塑,嚴重影響吞吐量和應用方的穩(wěn)定性。對于這種情況罐韩,首先判斷當前Redis的并發(fā)量是否達到極限憾赁,建議使用統(tǒng)計命令redis-cli -h {ip} -p {port} --stat獲取當前Redis使用情況
2.3 持久化阻塞
對于開啟了持久化功能的Redis節(jié)點,需要排查是否是持久化導致的阻塞散吵。
- fork阻塞:fork操作發(fā)生在RDB和AOF重寫時龙考,Redis主線程調用fork操作產生共享內存的子進程,由子進程完成持久化文件重寫工作错蝴。如果fork操作本身耗時過長洲愤,必然會導致主線程的阻塞。
- AOF刷盤阻塞:當我們開啟AOF持久化功能時顷锰,文件刷盤的方式一般采用每秒一次柬赐,后臺線程每秒對AOF文件做fsync操作。當硬盤壓力過大時官紫,fsync操作需要等待肛宋,直到寫入完成。如果主線程發(fā)現(xiàn)距離上一次的fsync成功超過2秒束世,為了數(shù)據(jù)安全性它會阻塞直到后臺線程執(zhí)行fsync操作完成酝陈。這種阻塞行為主要是硬盤壓力引起。
- HugePage寫操作阻塞:子進程在執(zhí)行重寫期間利用Linux寫時復制技術降低內存開銷毁涉,因此只有寫操作時Redis才復制要修改的內存頁沉帮。對于開啟Transparent HugePages的操作系統(tǒng),每次寫命令引起的復制內存頁單位由4K變?yōu)?MB贫堰,放大了512倍穆壕,會拖慢寫操作的執(zhí)行時間,導致大量寫操作慢查詢其屏。
三喇勋、外在原因
3.1 CPU競爭
- 進程競爭:Redis是典型的CPU密集型應用,不建議和其他多核CPU密集型服務部署在一起偎行。當其他進程過度消耗CPU時川背,將嚴重影響Redis吞吐量贰拿。可以通過top熄云、sar等命令定位到CPU消耗的時間點和具體進程膨更,這個問題比較容易發(fā)現(xiàn),需要調整服務之間部署結構缴允。
- 綁定CPU:部署Redis時為了充分利用多核CPU询一,通常一臺機器部署多個實例。常見的一種優(yōu)化是把Redis進程綁定到CPU上癌椿,用于降低CPU頻繁上下文切換的開銷。這個優(yōu)化技巧正常情況下沒有問題菱阵,但是存在例外情況踢俄,當Redis父進程創(chuàng)建子進程進行RDB/AOF重寫時,如果做了CPU綁定晴及,會與父進程共享使用一個CPU都办。子進程重寫時對單核CPU使用率通常在90%以上,父進程與子進程將產生激烈CPU競爭虑稼,極大影響Redis穩(wěn)定性琳钉。因此對于開啟了持久化或參與復制的主節(jié)點不建議綁定CPU。
3.2 內存交換
內存交換(swap)對于Redis來說是非常致命的蛛倦,Redis保證高性能的一個重要前提是所有的數(shù)據(jù)在內存中歌懒。如果操作系統(tǒng)把Redis使用的部分內存換出到硬盤,由于內存與硬盤讀寫速度差幾個數(shù)量級溯壶,會導致發(fā)生交換后的Redis性能急劇下降及皂。
預防內存交換:
- 保證機器充足的可用內存。
- 確保所有Redis實例設置最大可用內存(maxmemory)且改,防止極端情況下Redis內存不可控的增長验烧。
- 降低系統(tǒng)使用swap優(yōu)先級。
3.3 網(wǎng)絡問題
(1)連接拒絕
- 網(wǎng)絡閃斷(網(wǎng)絡割接或者帶寬耗盡)
- Redis連接拒絕(超過客戶端最大連接數(shù))
- 連接溢出(進程限制或backlog隊列溢出)
(2)網(wǎng)絡延遲
網(wǎng)絡延遲取決于客戶端到Redis服務器之間的網(wǎng)絡環(huán)境又跛。主要包括它們之間的物理拓撲和帶寬占用情況碍拆。常見的物理拓撲按網(wǎng)絡延遲由快到慢可分為:同物理機>同機架>跨機架>同機房>同城機房>異地機房。但它們容災性正好相反慨蓝,同物理機容災性最低而異地機房容災性最高感混。
網(wǎng)絡延遲問題經(jīng)常出現(xiàn)在跨機房的部署結構上,對于機房之間延遲比較嚴重的場景需要調整拓撲結構菌仁,如把客戶端和Redis部署在同機房或同城機房等浩习。
帶寬瓶頸通常出現(xiàn)在以下幾個方面:
- 機器網(wǎng)卡帶寬。
- 機架交換機帶寬济丘。
- 機房之間專線帶寬谱秽。
(3)網(wǎng)卡軟中斷
網(wǎng)卡軟中斷是指由于單個網(wǎng)卡隊列只能使用一個CPU洽蛀,高并發(fā)下網(wǎng)卡數(shù)據(jù)交互都集中在同一個CPU,導致無法充分利用多核CPU的情況疟赊。網(wǎng)卡軟中斷瓶頸一般出現(xiàn)在網(wǎng)絡高流量吞吐的場景郊供。
參考資料:
《Redis開發(fā)與運維》