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