1. Redis 概覽
Redis 和 memcache 的區別,Redis 支持的資料型別應用場景
-
redis 支持的資料結構更豐富(string,hash,list,set,zset),memcache 只支持 key-value 的存盤;
-
redis 原生支持集群,memcache 沒有原生的集群模式,
2. Redis 單執行緒模型
redis 單執行緒處理請求流程
redis 采用 IO 多路復用機制來處理請求,采用 reactor IO 模型, 處理流程如下:
-
首先接收到客戶端的 socket 請求,多路復用器將 socket 轉給連接應答處理器;
-
連接應答處理器將 AE_READABLE 事件與命令請求處理器關聯(這里是把 socket 事件放入一個佇列);
-
命令請求處理器從 socket 中讀到指令,再記憶體中執行,并將 AE_WRITEABLE 事件與命令回復處理器關聯;
-
命令回復處理器將結果回傳給 socket,并解除關聯,
redis 單執行緒效率高的原因
-
非阻塞 IO 復用(上圖流程), I/O 多路復用分派事件,事件處理器處理事件(這個可以理解為注冊的一段函式,定義了事件發生的時候應該執行的動作), 這里分派事件和處理事件其實都是同一個執行緒;
-
純記憶體操作效率高;
-
單執行緒反而避免了多執行緒切換,
3. Redis 過期策略
- 對 key 設定有效期,redis 的洗掉策略: 定期洗掉+惰性洗掉,
-
定期洗掉指的是 redis 默認每 100ms 就隨機抽取一些設定了過期事件的 key ,檢查是否過期,如果過期就洗掉,如果 redis 設定了 10 萬個 key 都設定了過期時間,每隔幾百毫秒就要檢查 10 萬個 key 那 CPU 負載就很高了,所以 redis 并不會每隔 100ms 就檢查所有的 key,而是隨機抽取一些 key 來檢查,
-
但這樣會導致有些 key 過期了并沒有被洗掉,所以采取了惰性洗掉,意思是在獲取某個 key 的時候發現過期了,如果 key 過期了就洗掉掉不會回傳,
這兩個策略結合起來保證過期的 key 一定會被洗掉,
- 最大記憶體淘汰(maxmemory-policy)
如果 redis 記憶體占用太多,就會進行記憶體淘汰,有如下策略:
-
noeviction: 如果記憶體不足以寫入資料, 新寫入操作直接報錯;
-
allkeys-lru: 記憶體不足以寫入資料,移除最近最少使用的 key(最常用的策略);
-
allkeys-random: 記憶體不足隨機移除幾個 key;
-
volatile-lru: 在設定了過期時間的 key 中,移除最近最少使用;
-
volatile-random: 設定了過期的時間的 key 中,隨機移除幾個,
4. Redis 主從模式保證高并發和高可用(哨兵模式)
讀寫分離
單機的 Redis 的 QPS 大概就在上萬到幾萬不等,無法承受更高的并發,
讀寫分離保證高并發(10W+ QPS):對于快取來說一般都是支撐高并發讀,寫請求都是比較少的,采用讀寫分離的架構(一主多從),master 負責接收寫請求,資料同步到 slave 上提供讀服務,如果遇到瓶頸只需要增加 slave 機器就可以水平擴容
主從復制機制
redis replication 機制:
-
redis 采取異步復制到 slave 節點;
-
slave 節點做復制操作的時候是不會 block 自己的,它會使用舊的資料集來提供服務,復制,完成后,洗掉舊的資料集,加載新的資料集,這個時候會暫停服務(時間很短暫);
-
如果采用了主從架構,master 需要開啟持久化,如果 master 沒有開啟持久化(rdb 和 aof 都關閉了),master 宕機重啟后資料是空的,然后經過復制就把所有 slave 的資料也弄丟了,
即使采用高可用的的哨兵機制,可能 sentinal 還沒有檢測到 master failure,master 就自動重啟了,還是會導致 slave 清空故障,
主從同步流程
-
當 slave 啟動時會發送一個 psync 命令給 master;
-
如果是重新連接 master,則 master node 會復制給 slave 缺少的那部分資料;
-
如果是 slave 第一次連接 master,則會觸發一次全量復制(full resynchronization),開始 full resynchronization 的時候,master 會生成一份 rdb 快照,同時將客戶端命令快取在記憶體,rdb 生成完后,就發送給 slave,slave 先寫入磁盤在加載到記憶體,然后 master 將快取的命令發送給 slave,

哨兵(sentinal)模式介紹
哨兵是 redis 集群架構的一個重要組件,主要提供如下功能:
-
集群監控:負責監控 master 和 slave 是否正常作業;
-
訊息通知:如果某個 redis 實體有故障, 哨兵負責發訊息通知管理員;
-
故障轉移: 如果 master node 發生故障,會自動切換到 slave;
-
配置中心:如果故障轉移發生了,通知客戶端新的 master 地址,
哨兵的核心知識:
-
哨兵至少三個,保證自己的高可用;
-
哨兵+主從的部署架構是用來保證 redis 集群高可用的,并非保證資料不丟失;
-
哨兵(Sentinel)需要通過不斷的測驗和觀察才能保證高可用,
為什么哨兵只有兩個節點無法正常作業

假設哨兵集群只部署了 2 個哨兵實體,quorum=1,
master 宕機的時候,s1 和 s2 只要有一個哨兵認為 master 宕機 j 就可以進行切換,并且會從 s1 和 s2 中選取一個來進行故障轉移,這個時候是需要滿足 majority,也就是大多數哨兵是運行的,2 個哨兵的 majority 是 2,如果 2 個哨兵都運行著就允許執行故障轉移,如果 M1 所在的機器宕機了,那么 s1 哨兵也就掛了,只剩 s2 一個,沒有 majorityl 來允許執行故障轉移,雖然集群還有一臺機器 R1,但是故障轉移也不會執行,
如果是經典的三哨兵集群,如下:

此時 majority 也是 2,就算 M1 所在的機器宕機了,哨兵還是剩下兩個 s2 和 s3,它們滿足 majority 就可以允許故障轉移執行,
哨兵核心底層原理
- sdown 和 odown 兩種失敗狀態;
-
sdown 是主觀宕機,就是一個哨兵覺得 master 宕機了,達成條件是如果一個哨兵 ping master 超過了 is-master-down-after-milliseconds 指定的毫秒數后就認為主觀宕機;
-
odown 是客觀宕機,如果一個哨兵在指定時間內收到了 majority(大多數) 數量的哨兵也認為那個 master 宕機了,就是客觀宕機,
- 哨兵之間的互相發現:哨兵是通過 redis 的 pub/sub 實作的,
5. Redis 資料的恢復(Redis 的持久化)
RDB
RDB 原理
RDB(Redis DataBase)是將某一個時刻的記憶體快照(Snapshot),以二進制的方式寫入磁盤的程序,
RDB 有兩種方式 save 和 bgsave:
-
save: 執行就會觸發 Redis 的持久化,但同時也是使 Redis 處于阻塞狀態,直到 RDB 持久化完成,才會回應其他客戶端發來的命令;
-
bgsave: bgsave 會 fork() 一個子行程來執行持久化,整個程序中只有在 fork() 子行程時有短暫的阻塞,當子行程被創建之后,Redis 的主行程就可以回應其他客戶端的請求了,
RDB 配置
除了使用 save 和 bgsave 命令觸發之外, RDB 支持自動觸發,
自動觸發策略可配置 Redis 在指定的時間內,資料發生了多少次變化時,會自動執行 bgsave 命令,在 redis 組態檔中配置:
在時間 m 秒內,如果 Redis 資料至少發生了 n 次變化,那么就自動執行BGSAVE命令,
save m n
RDB 優缺點
- RDB 的優點:
-
RDB 會定時生成多個資料檔案,每個資料檔案都代表了某個時刻的 redis 全量資料,適合做冷備,可以將這個檔案上傳到一個遠程的安全存盤中,以預定好的策略來定期備份 redis 中的資料;
-
RDB 對 redis 對外提供讀寫服務的影響非常小,redis 是通過 fork 主行程的一個子行程操作磁盤 IO 來進行持久化的;
-
相對于 AOF,直接基于 RDB 來恢復 reids 資料更快,
- RDB 的缺點:
-
如果使用 RDB 來恢復資料,會丟失一部分資料,因為 RDB 是定時生成的快照檔案;
-
RDB 每次來 fork 出子行程的時候,如果資料檔案特別大,可能會影響對外提供服務,暫停數秒(主行程需要拷貝自己的記憶體表給子行程, 實體很大的時候這個拷貝程序會很長),latest_fork_usec 代表 fork 導致的延時;Redis 上執行 INFO 命令查看 latest_fork_usec;當 RDB 比較大的時候, 應該在 slave 節點執行備份, 并在低峰期執行,
AOF
AOF 原理
redis 對每條寫入命令進行日志記錄,以 append-only 的方式寫入一個日志檔案,redis 重啟的時候通過重放日志檔案來恢復資料集,(由于運行久了 AOF 檔案會越來越大,redis 提供一種 rewrite 機制,基于當前記憶體中的資料集,來構建一個更小的 AOF 檔案,將舊的龐大的 AOF 檔案洗掉),rewrite 即把日志檔案壓縮, 通過 bgrewriteaof 觸發重寫,AOF rewrite 后臺執行的方式和 RDB 有類似的地方,fork 一個子行程,主行程仍進行服務,子行程執行 AOF 持久化,資料被 dump 到磁盤上,與 RDB 不同的是,后臺子行程持久化程序中,主行程會記錄期間的所有資料變更(主行程還在服務),并存盤在 server.aof_rewrite_buf_blocks 中;后臺子行程結束后,Redis 更新快取追加到 AOF 檔案中,是 RDB 持久化所不具備的,
AOF 的作業流程如下:
-
Redis 執行寫命令后,把這個命令寫入到 AOF 檔案記憶體中(write 系統呼叫);
-
Redis 根據配置的 AOF 刷盤策略,把 AOF 記憶體資料刷到磁盤上(fsync 系統呼叫);
-
根據 rewrite 相關的配置觸發 rewrite 流程,
AOF 配置
-
appendonly: 是否啟用 AOF(yes | no);
-
appendfsync: 刷盤的機制:
-
always:主執行緒每次執行寫操作后立即刷盤,此方案會占用比較大的磁盤 IO 資源,但資料安全性最高;
-
everysec:主執行緒每次寫操作只寫記憶體就回傳,然后由后臺執行緒每隔 1 秒執行一次刷盤操作(觸發 fsync 系統呼叫),此方案對性能影響相對較小,但當 Redis 宕機時會丟失 1 秒的資料;
-
no:主執行緒每次寫操作只寫記憶體就回傳,記憶體資料什么時候刷到磁盤,交由作業系統決定,此方案對性能影響最小,但資料安全性也最低,Redis 宕機時丟失的資料取決于作業系統刷盤時機,
-
auto-aof-rewrite-percentage: 當 aof 檔案相較于上一版本的 aof 檔案大小的百分比達到多少時觸發 AOF 重寫,舉個例子,auto-aof-rewrite-percentage 選項配置為 100,上一版本的 aof 檔案大小為 100M,那么當我們的 aof 檔案達到 200M 的時候,觸發 AOF 重寫;
-
auto-aof-rewite-min-size:最小能容忍 aof 檔案大小,超過這個大小必須進行 AOF 重寫;
-
no-appendfsync-on-rewrite: 設定為 yes 表示 rewrite 期間對新寫操作不 fsync,暫時存在記憶體中,等 rewrite 完成后再寫入,默認為 no,
AOF 優缺點
- AOF 的優點:
-
可以更好的保證資料不丟失,一般 AOF 每隔 1s 通過一個后臺執行緒來執行 fsync(強制重繪磁盤頁快取),最多丟失 1s 的資料;
-
AOF 以 append-only 的方式寫入(順序追加),沒有磁盤尋址開銷,性能很高;
-
AOF 即使檔案很大, 觸發后臺 rewrite 的操作的時候一般也不會影響客戶端的讀寫,(rewrite 的時候會對其中指令進行壓縮,創建出一份恢復需要的最小日志出來),
在創建新的日志檔案的時候,老的檔案還是照常寫入,當新的檔案創建完成后再交換新老日志,但是還是有可能會影響到主執行緒的寫入, 如:

當磁盤的 IO 負載很高,那這個后臺執行緒在執行 AOF fsync 刷盤操作(fsync 系統呼叫)時就會被阻塞住, ,緊接著,主執行緒又需要把資料寫到檔案記憶體中(write 系統呼叫),但此時的后臺子執行緒由于磁盤負載過高,導致 fsync 發生阻塞,遲遲不能回傳,那主執行緒在執行 write 系統呼叫時,也會被阻塞住,直到后臺執行緒 fsync 執行完成后,主執行緒執行 write 才能成功回傳,這時候主執行緒就無法回應客戶端的請求, 可能會導致客戶端請求 redis 超時,具體類似: https://blog.csdn.net/mmgithub123/article/details/124507846,
- AOF 日志檔案通過非常可讀的方式進行記錄,這個特性適合做災難性的誤操作的緊急恢復,比如不小心使用 flushall 清空了所有資料,只要 rewrite 沒有發生,就可以立即拷貝 AOF,將最后一條 flushall 命令洗掉,再回放 AOF 恢復資料,
- AOF 的缺點:
-
同一份資料,因為 AOF 記錄的命令會比 RDB 快照檔案更大;
-
AOF 開啟后,支持寫的 QPS 會比 RDB 支持寫的 QPS 要低,畢竟 AOF 有寫磁盤的操作,
總結
總結 AOF 和 RDB 該如何選擇:兩者綜合使用,將 AOF 配置成每秒 fsync 一次,RDB 作為冷備,AOF 用來保證資料不丟失的恢復第一選擇,當 AOF 檔案損壞或不可用的時候還可以使用 RDB 來快速恢復,
6. Redis 集群模式(redis cluster)
在主從部署模式上,雖然實作了一定程度的高并發,并保證了高可用,但是有如下限制:
-
master 資料和 slave 資料一模一樣,master 的資料量就是集群的限制瓶頸;
-
redis 集群的寫能力也受到了 master 節點的單機限制,
在高版本的 Redis 已經原生支持集群(cluster)模式,可以多 master 多 slave 部署,橫向擴展 Redis 集群的能力,Redis Cluster 支持 N 個 master node ,每個 master node 可以掛載多個 slave node,
redis cluster 介紹
-
自動將資料切片,每個 master 上放一部分資料;
-
提供內置的高可用支持,部分 master 不可用時還是能夠作業;
-
redis cluster 模式下,每個 redis 要開放兩個埠:6379 和 10000+以后的埠(如 16379),16379 是用來節點之間通信的,使用的是 cluster bus 集群總線,cluster bus 用來做故障檢測,配置更新,故障轉移授權,
redis cluster 負載均衡
redis cluster 采用 一致性 hash+虛擬節點 來負載均衡,redis cluster 有固定的 16384 個 slot (2^14),對每個 key 做 CRC16 值計算,然后對 16384 mod,可以獲取每個 key 的 slot,redis cluster 每個 master 都會持有部分 slot,比如 三個 master 那么 每個 master 就會持有 5000 多個 slot,hash slot 讓 node 的添加和洗掉變得很簡單,增加一個 master,就將其他 master 的 slot 移動部分過去,減少一個就分給其他 master,這樣讓集群擴容的成本變得很低,
cluster 基礎通信原理(gossip 協議)
與集中式不同(如使用 zookeeper 進行分布式協調注冊),redis cluster 使用的是 gossip 協議進行通信,并不是將集群元資料存盤在某個節點上,而是不斷的互相通信,保持整個集群的元資料是完整的,gossip 協議所有節點都持有一份元資料,不同節點的元資料發生了變更,就不斷的將元資料發送給其他節點,讓其他節點也進行元資料的變更,
集中式的好處:元資料的讀取和更新時效性很好,一旦元資料變化就更新到集中式存盤,缺點就是元資料都在一個地方,可能導致元資料的存盤壓力,
對于 gossip 來說:元資料的更新會有延時,會降低元資料的壓力,缺點是操作是元資料更新可能會導致集群的操作有一些滯后,
redis cluster 主備切換與高可用
-
判斷節點宕機:如果有一個節點認為另外一個節點宕機,那就是 pfail,主觀宕機,如果多個節點認為一個節點宕機,那就是 fail,客觀宕機,跟哨兵的原理一樣;
-
對宕機的 master,從其所有的 slave 中選取一個切換成 master node,再次之前會進行一次過濾,檢查每個 slave 與 master 的斷開時間,如果超過了 cluster-node-timeout * cluster-slave-validity-factor 就沒有資格切換成 master;
-
從節點選取:每個從節點都會根據從 master 復制資料的 offset,來設定一個選舉時間,offset 越大的從節點,選舉時間越靠前,master node 開始給 slave 選舉投票,如果大部分 master(n/2+1)都投給了某個 slave,那么選舉通過(與 zk 有點像,選舉時間類似于 epochid);
-
整個流程與哨兵類似,可以說 redis cluster 集成了哨兵的功能,更加的強大;
-
Redis 集群部署相關問題 redis 機器的配置,多少臺機器,能達到多少 qps?
- 機器標準:8 核+32G
- 集群: 5 主+5 從(每個 master 都掛一個 slave)
- 效果: 每臺機器最高峰每秒大概 5W,5 臺機器最多就是 25W,每個 master 都有一個從節點,任何一個節點掛了都有備份可切換成主節點進行故障轉移
-
腦裂問題哨兵模式下:
- master 下 掛載了 3 個 slave,如果 master 由于網路抖動被哨兵認為宕機了,執行了故障轉移,從 slave 里面選取了一個作為新的 master,這個時候老的 master 又恢復了,剛好又有 client 連的還是老的 master,就會產生腦裂,資料也會不一致,比如 incr 全域 id 也會重復,
- redis 對此的解決方案是:min-slaves-to-write 1 至少有一個 slave 連接 min-slaves-max-lag 10 slave 與 master 主從復制延遲時間如果連接到 master 的 slave 數小于最少 slave 的數量,并且主從復制延遲時間超過配置時間,master 就拒絕寫入 12,client 連接 redis 多 tcp 連接的考量首先 redis server 雖然是單執行緒來處理請求, 但是他是多路復用的, 單 tcp 連接肯定是沒有多 tcp 連接性能好, 多路復用一個 io 周期得到的就緒 io 事件越多, 處理的就越多,這也不是絕對的, 如果使用 pipeline 的方式傳輸, 單連接會比多連接性能好, 因為每一個 pipeline 的單次請求過多也會導致單周期到的命令太多, 性能下降多少個連接比較合適這個問題, redis cluser 控制在每個節點 100 個連接以內,
作者:leobhao
本文來自博客園,作者:古道輕風,轉載請注明原文鏈接:https://www.cnblogs.com/88223100/p/Summary-of-Redis-knowledge.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/516469.html
標籤:其他


