Redis相關知識點小結

在這裏插入圖片描述

前言

最近重新溫習了redis的相關內容,週末淨下心總結,如果又覺得異議的地方,歡迎下方留言評論。

redis常見數據結構

  • 字符串String、字典Hash、列表List、集合Set、有序集合SortedSet。

如果你是Redis中高級用戶,還需要加上下面幾種數據結構HyperLogLog、Geo、Pub/Sub。
如果你說還玩過Redis Module,像BloomFilter,RedisSearch,Redis-ML,面試官得眼睛就開始發亮了。

使用keys指令可以掃出指定模式的key列表

如果這個redis正在給線上的業務提供服務,那使用keys指令會有什麼問題?

redis的單線程的。keys指令會導致線程阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。這個時候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定的重複概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長。

redis 主從模式,哨兵模式,cluster

瞭解redis同步機制之前,我們知道CAP原則,網絡分區發生時,一致性和可用性兩難全,redis這裏的策略選擇就是可用性。

主從模式

Redis可以使用主從同步,從從同步。第一次同步時,主節點做一次bgsave,並同時將後續修改操作記錄到內存buffer,待完成後將rdb文件全量同步到複製節點,複製節點接受完成後將rdb鏡像加載到內存。加載完成後,再通知主節點將期間修改的操作記錄同步到複製節點進行重放就完成了同步過程。

面試高開,我覺的還得知道以下幾條

  • 增量複製情況下,主節點的buffer是一個定長的環形數組,如果數組內容滿了,就會從頭開始覆蓋前面的內容,那麼就要知道快照複製。
  • 快照複製,就是bgsave將當前內存的數據全部快照到磁盤文件中,然後再將快照文件的內容全部傳送到從節點,從節點將快照文件接受完畢後,立即執行一次全量加載,加載之前先要將當前內存的數據清空。加載完畢後通知主節點繼續進行增量同步。
  • 快照複製還會出現一個問題,就是你的buffer設置的太小,導致指令被覆蓋,這樣就行成了快照同步死循環。所以設置buffer大小很重要。
  • 無盤複製是指主服務器直接通過套接字將快照內容發送到從節點,從節點還是跟之前一樣,先將接收到的內容存儲到磁盤文件中,再進行一次性加載。

哨兵模式

Redis Sentinal着眼於高可用,在master宕機時會自動將slave提升爲master,繼續提供服務。

cluster

當 Redis Cluster 的客戶端來連接集羣時,它也會得到一份集羣的槽位配置信息。這樣當客戶端要查找某個 key 時,可以直接定位到目標節點。

Redis Cluster着眼於擴展性,在單個redis內存不足時,使用Cluster進行分片存儲。

redis過期策略

  • 被動刪除:當讀/寫一個已經過期的key時,會發生一個惰性刪除,直接刪掉這個過期的key。
  • 主動刪除:由於惰性刪除的策略無法保證過期數據被及時刪掉,所以redis會定期主動淘汰一批已經過期的key
  • 當前已用的內存超過maxmemory限定時,觸發主動情理策略。

當 Redis 內存超出物理內存限制時,內存的數據會開始和磁盤產生頻繁的交換 (swap)。這種情況倒是redis性能下降,redis提供集中策略騰出空間

  • volatile-lru : 只對設置了過期時間的key進行LRU (默認值) (對已經設置了過期時間的數據結構,挑選最少使用的數據淘汰)
  • allkeys-lru: 刪除LRU算法的key(從全部的數據結構,挑選最少使用的數據淘汰)
  • volatile-random : 隨機刪除即將過期的key(對已經設置了過期時間的數據,採用隨機刪除的數據淘汰)
  • allkeys-random : 隨機刪除(對所有的數據,採用隨機刪除的數據淘汰)
  • volatile-ttl :刪除即將過期的(只對設置了過期時間的數據,採用挑選將要過期的數據淘汰)
  • noeviction :永不過期(這個清理過程是阻塞的)

緩存穿透,緩存擊穿,緩存雪崩解決方案分析

緩存穿透,緩存擊穿,緩存雪崩解決方案分析

爲什麼說Redis是單線程的以及Redis爲什麼這麼快

1、完全基於內存,絕大部分請求是純粹的內存操作,非常快速。數據存在內存中,類似於HashMap,HashMap的優勢就是查找和操作的時間複雜度都是O(1);

2、數據結構簡單,對數據操作也簡單,Redis中的數據結構是專門進行設計的;

3、採用單線程,避免了不必要的上下文切換和競爭條件,也不存在多進程或者多線程導致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因爲可能出現死鎖而導致的性能消耗;

4、使用多路I/O複用模型,非阻塞IO;

5、使用底層模型不同,它們之間底層實現方式以及與客戶端之間通信的應用協議不一樣,Redis直接自己構建了VM 機制 ,因爲一般的系統調用系統函數的話,會浪費一定的時間去移動和請求;
以上幾點都比較好理解,下邊我們針對多路 I/O 複用模型進行簡單的探討:

多路I/O複用模型是利用 select、poll、epoll 可以同時監察多個流的 I/O 事件的能力,在空閒的時候,會把當前線程阻塞掉,當有一個或多個流有 I/O 事件時,就從阻塞態中喚醒,於是程序就會輪詢一遍所有的流(epoll 是隻輪詢那些真正發出了事件的流),並且只依次順序的處理就緒的流,這種做法就避免了大量的無用操作。

這裏“多路”指的是多個網絡連接,“複用”指的是複用同一個線程。採用多路 I/O 複用技術可以讓單個線程高效的處理多個連接請求(儘量減少網絡 IO 的時間消耗),且 Redis 在內存中操作數據的速度非常快,也就是說內存內的操作不會成爲影響Redis性能的瓶頸,主要由以上幾點造就了 Redis 具有很高的吞吐量。

基於Redis的分佈式鎖實現、和zk實現有何區別

redis實現分佈式鎖原理

先拿setnx來爭搶鎖,搶到之後,再用expire給鎖加一個過期時間防止鎖忘記了釋放。

這時候對方會告訴你說你回答得不錯,然後接着問如果在setnx之後執行expire之前進程意外crash或者要重啓維護了,那會怎麼樣?

這個鎖就永遠得不到釋放了,set指令有非常複雜的參數,這個應該是可以同時把setnx和expire合成一條指令來用的(set lock:codehole true ex 5 nx OK)

如果在加鎖和釋放鎖之間的邏輯執行的太長,以至於超出了鎖的超時限制,這個時候就會導致多個線程獲取鎖,怎麼解決?

  1. 爲了避免這個問題,Redis 分佈式鎖不要用於較長時間的任務。
  2. 可以考慮使用redssion,它有一個看門狗策略,可以給快要超時的業務續期時間。

解鎖的時候能直接del麼,爲什麼,怎麼解決?
答案肯定是不行的。

  1. 這種不先判斷鎖的擁有者而直接解鎖的方式,會導致任何客戶端都可以隨時進行解鎖,即使這把鎖不是它的。
  2. 使用Lua腳本setnx把value也設置上,釋放鎖時先匹配隨機數是否一致,然後再刪除 key。但是匹配 value 和刪除 key 不是一個原子操作 ,這個時候使用lua腳本,jedis有eval命令,保證原子性。

那使用主從模式的時候,主節點加了鎖,從節點還沒有同步到,這個時候會導致多個客戶端拿到鎖,怎麼解決?

  1. Redlock算法,加鎖時,它會向過半節點發送 set(key, value, nx=True, ex=xxx) 指令,只要過半節點 set 成功,那就認爲加鎖成功。釋放鎖時,需要向所有節點發送 del 指令。(補充:從庫不會進行過期掃描,而是異步等主庫的del指令,所以會出現數據主從不一致)

zk實現分佈式鎖

TODO 這個開專門學習下,有待更新

使用redis實現限流

使用過Redis做異步隊列麼

一般使用list結構作爲隊列,rpush生產消息,lpop消費消息。當lpop沒有消息的時候,要適當sleep一會再重試。

如果對方追問可不可以不用sleep呢?
list還有個指令叫blpop,在沒有消息的時候,它會阻塞住直到消息到來。

如果對方追問能不能生產一次消費多次呢?
使用pub/sub主題訂閱者模式,可以實現1:N的消息隊列。

如果對方追問pub/sub有什麼缺點?
在消費者下線的情況下,生產的消息會丟失,得使用專業的消息隊列如rabbitmq等。

如果對方追問redis如何實現延時隊列?

使用sortedset,拿時間戳作爲score,消息內容作爲key調用zadd來生產消息,消費者用zrangebyscore指令獲取N秒之前的數據輪詢進行處理。

Redis如何做持久化的

bgsave做鏡像全量持久化,aof做增量持久化。因爲bgsave會耗費較長時間,不夠實時,在停機的時候會導致大量丟失數據,所以需要aof來配合使用。在redis實例重啓時,會使用bgsave持久化文件重新構建內存,再使用aof重放近期的操作指令來實現完整恢復重啓之前的狀態。

對方追問那如果突然機器掉電會怎樣?

取決於aof日誌sync屬性的配置,如果不要求性能,在每條寫指令時都sync一下磁盤,就不會丟失數據。但是在高性能的要求下每次都sync是不現實的,一般都使用定時sync,比如1s1次,這個時候最多就會丟失1s的數據。

對方追問bgsave的原理是什麼?

你給出兩個詞彙就可以了,fork和cow。fork是指redis通過創建子進程來進行bgsave操作,cow指的是copy on write,子進程創建後,父子進程共享數據段,父進程繼續提供讀寫服務,寫髒的頁面數據會逐漸和子進程分離開來。

Pipeline有什麼好處,爲什麼要用pipeline

可以將多次IO往返的時間縮減爲一次,前提是pipeline執行的指令之間沒有因果相關性。

Redis的rehash

rehash是一個比較複雜的過程,爲了不阻塞Redis的進程,它採用了一種漸進式的rehash的機制。

rehash過程中,讀取、刪除、更新是怎麼樣的?
因爲在進行漸進式 rehash 的過程中, 字典會同時使用 ht[0] 和 ht[1] 兩個哈希表, 所以在漸進式 rehash 進行期間, 字典的刪除(delete)、查找(find)、更新(update)等操作會在兩個哈希表上進行: 比如說, 要在字典裏面查找一個鍵的話, 程序會先在 ht[0] 裏面進行查找, 如果沒找到的話, 就會繼續到 ht[1] 裏面進行查找, 諸如此類。

rehash過程中,增加數據是怎麼樣的?
在漸進式 rehash 執行期間, 新添加到字典的鍵值對一律會被保存到 ht[1] 裏面, 而 ht[0] 則不再進行任何添加操作: 這一措施保證了 ht[0] 包含的鍵值對數量會只減不增, 並隨着 rehash 操作的執行而最終變成空表。

漸進式rehash帶來的問題

漸進式rehash避免了redis阻塞,可以說非常完美,但是由於在rehash時,需要分配一個新的hash表,在rehash期間,同時有兩個hash表在使用,會使得redis內存使用量瞬間突增,在Redis 滿容狀態下由於Rehash會導致大量Key驅逐。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章