Redis 原理


 

線程IO模型

Redis 是個單線程程序!(對外網絡請求服務)

對於那些時間復雜度為 O(n) 級別的指令,一定要謹慎使用,一不小心就可能會導致 Redis 卡頓。

使用緩沖區,事件輪詢 API(Linux操作系統提供的select,poll,epoll),非阻塞 IO(能讀多少讀多少,能寫多少寫多少,讀方法和寫方法都會通過返回值來告知程序實際讀寫了多少字節)。

注:epoll無須遍歷整個被偵聽的描述符集(fd),只要遍歷那些被內核IO事件異步喚醒的描述符集合,select與poll是全輪詢。

 

通信協議

Redis 將所有數據都放在內存,用一個單線程對外提供服務,單個節點在跑滿一個 CPU 核心的情況下可以達到大約 10w/s 的 QPS(每秒查詢數)。

RESP(Redis Serialization Protocol)

RESP 是 Redis 序列化協議的簡寫。它是一種直觀的文本協議,優勢在於實現異常簡單,解析性能極好。

Redis 協議將傳輸的結構數據分為 5 種最小單元類型,單元結束時統一加上回車換行符號\r\n

  1. 單行字符串 以 + 符號開頭。
  2. 多行字符串 以 $ 符號開頭,后跟字符串長度。
  3. 整數值 以 : 符號開頭,后跟整數的字符串形式。
  4. 錯誤消息 以 - 符號開頭。
  5. 數組 以 * 號開頭,后跟數組的長度。

 

持久化

Redis 的數據全部在內存里,如果突然宕機,數據就會全部丟失,因此必須有一種機制來保證 Redis 的數據不會因為故障而丟失,這種機制就是 Redis 的持久化機制。

Redis 的持久化機制有兩種,第一種是快照,第二種是 AOF 日志。快照是一次全量備份,AOF 日志是連續的增量備份。

快照原理

Redis 在持久化時會調用 glibc 的函數fork產生一個子進程,快照持久化完全交給子進程來處理,父進程繼續處理客戶端請求。子進程剛剛產生時,它和父進程共享內存里面的代碼段和數據段。

fork函數會在父子進程同時返回,在父進程里返回子進程的 pid,在子進程里返回零。如果操作系統內存資源不足,pid 就會是負數,表示fork失敗。 

這個時候就會使用操作系統的 COW (copy on write)機制來進行數據段頁面的分離。數據段是由很多操作系統的頁面(一個頁面4k)組合而成,當父進程對其中一個頁面的數據進行修改時,會將被共享的頁面復制一份分離出來,然后對這個復制的頁面進行修改。這時子進程相應的頁面是沒有變化的,還是進程產生時那一瞬間的數據。

AOF原理

AOF 日志存儲的是 Redis 服務器的順序指令序列,AOF 日志只記錄對內存進行修改的指令記錄。 假設 AOF 日志記錄了自 Redis 實例創建以來所有的修改性指令序列,那么就可以通過對一個空的 Redis 實例順序執行所有的指令,也就是「重放」,來恢復 Redis 當前實例的內存數據結構的狀態。

Redis 在長期運行的過程中,AOF 的日志會越變越長。如果實例宕機重啟,重放整個 AOF 日志會非常耗時,導致長時間 Redis 無法對外提供服務。所以需要對 AOF 日志重寫瘦身。

AOF 日志一般先暫存在內存緩存中,然后內核會異步將日志數據刷回磁盤,如果機器突然宕機,AOF 日志內容可能還沒有來得及完全刷到磁盤中,這個時候就會出現日志丟失。

Linux 的glibc提供了fsync(int fd)函數可以將指定文件的內容強制從內核緩存刷到磁盤。只要 Redis 進程實時調用 fsync 函數就可以保證 aof 日志不丟失。但是 fsync 是一個磁盤 IO 操作,它很慢!如果 Redis 執行一條指令就要 fsync 一次,那么 Redis 高性能的地位就不保了。 所以在生產環境的服務器中,Redis 通常是每隔 1s 左右執行一次 fsync 操作,周期 1s 是可以配置的。這是在數據安全性和性能之間做了一個折中,在保持高性能的同時,盡可能使得數據少丟失。

通常 Redis 的主節點是不會進行持久化操作,持久化操作主要在從節點進行。從節點是備份節點,沒有來自客戶端請求的壓力,它的操作系統資源往往比較充沛。

Redis 4.0 混合持久化

重啟 Redis 時,我們很少使用 rdb 來恢復內存狀態,因為會丟失大量數據。我們通常使用 AOF 日志重放,但是重放 AOF 日志性能相對 rdb 來說要慢很多,這樣在 Redis 實例很大的情況下,啟動需要花費很長的時間。

Redis 4.0 為了解決這個問題,帶來了一個新的持久化選項——混合持久化。將 rdb 文件的內容和增量的 AOF 日志文件存在一起。這里的 AOF 日志不再是全量的日志,而是自持久化開始到持久化結束的這段時間發生的增量 AOF 日志,通常這部分 AOF 日志很小。

於是在 Redis 重啟的時候,可以先加載 rdb 的內容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重啟效率因此大幅得到提升。

 

管道

本質節省的是網絡響應的時間開銷,客戶端一次發送多條命令,服務端照常一條一條處理,不過客戶端等待響應的時間少了。

之所以能節省網絡響應的時間開銷,在於客戶端寫讀的本質,是和本地操作系統內核打交道,寫幾乎不耗時,讀需要等待響應耗時。

 

事務

Redis 的事務沒有滿足原子性,只滿足了事務的隔離性。指令:multi,discard,exec。

所以通常 Redis 的客戶端在執行事務時都會結合 pipeline 一起使用,這樣可以將多次 IO 操作壓縮為單次 IO 操作。

Redis是先執行指令然后做日志,所以不支持回滾。

分布式鎖是一種悲觀鎖,可以通過 watch 的機制實現樂觀鎖。

 

 

— —總結自:《Redis深度歷險》

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM