這是我參與8月更文挑戰的第8天,活動詳情查看:8月更文挑戰面試
Redis 做爲一個高性能的key-value數據庫。 redis的出現,很大程度補償了memcached這類keyvalue存儲的不足,在部分場合能夠對關係數據庫起到很好的補充做用。redis
在此抽時間整理了下 Redis 相關知識點,便於你們查漏補缺,不斷優化完善。算法
緩存sql
共享 Session數據庫
消息隊列系統後端
分佈式鎖緩存
純內存操做服務器
單線程操做,避免了頻繁的上下文切換markdown
合理高效的數據結構數據結構
採用了非阻塞 I/O 多路複用機制(有一個文件描述符同時監聽多個文件描述符是否有數據到來)
String 字符串:字符串類型是 Redis 最基礎的數據結構,首先鍵都是字符串類型,並且 其餘幾種數據結構都是在字符串類型基礎上構建的,咱們常使用的 set key value 命令就是字符串。經常使用在緩存、計數、共享 Session、限速等。
Hash 哈希:在 Redis 中,哈希類型是指鍵值自己又是一個鍵值對結構,哈希能夠用來存放用戶信息,好比實現購物車。
List 列表(雙向鏈表):列表(list)類型是用來存儲多個有序的字符串。能夠作簡單的消息隊列的功能。
Set 集合:集合(set)類型也是用來保存多個的字符串元素,但和列表類型不一 樣的是,集合中不容許有重複元素,而且集合中的元素是無序的,不能經過索引下標獲取元素。利用 Set 的交集、並集、差集等操做,能夠計算共同喜愛,所有的喜愛,本身獨有的喜愛等功能。
Sorted Set 有序集合(跳錶實現):Sorted Set 多了一個權重參數 Score,集合中的元素可以按 Score 進行排列。能夠作排行榜應用,取 TOP N 操做。
Redis 中數據過時策略採用按期刪除+惰性刪除策略
按期刪除策略:Redis 啓用一個定時器定時監視全部的 key,判斷 key 是否過時,過時的話就刪除。這種策略能夠保證過時的 key 最終都會被刪除,可是也存在嚴重的缺點:每次都遍歷內存中全部的數據,很是消耗 CPU 資源,而且當 key 已過時,可是定時器還處於未喚起狀態,這段時間內 key 仍然能夠用。
惰性刪除策略:在獲取 key 時,先判斷 key 是否過時,若是過時則刪除。這種方式存在一個缺點:若是這個 key 一直未被使用,那麼它一直在內存中,其實它已通過期了,會浪費大量的空間。
這兩種策略自然的互補,結合起來以後,定時刪除策略就發生了一些改變,再也不是每次掃描所有的 key 了,而是隨機抽取一部分 key 進行檢查,這樣就下降了對 CPU 資源的損耗,惰性刪除策略互補了未檢查到的 key,基本上知足了全部要求。可是有時候就是那麼的巧,既沒有被定時器抽取到,又沒有被使用,這些數據又如何從內存中消失?不要緊,還有內存淘汰機制,當內存不夠用時,內存淘汰機制就會上場。淘汰策略分爲:當內存不足以容納新寫入數據時,新寫入操做會報錯。(Redis 默認策略)當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的 Key。(LRU 推薦使用)當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個 Key。當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,移除最近最少使用的 Key。這種狀況通常是把 Redis 既當緩存,又作持久化存儲的時候才用。當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,隨機移除某個 Key。當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,有更早過時時間的 Key 優先移除。
Redis 中 setnx 不支持設置過時時間,作分佈式鎖時要想避免某一客戶端中斷致使死鎖,需設置 lock 過時時間,在高併發時 setnx 與 expire 不能實現原子操做,若是要用,得在程序代碼上顯示的加鎖。使用 SET 代替 SETNX ,至關於 SETNX+EXPIRE 實現了原子性,沒必要擔憂 SETNX 成功,EXPIRE 失敗的問題。
傳統的 LRU 是使用棧的形式,每次都將最新使用的移入棧頂,可是用棧的形式會致使執行 select *的時候大量非熱點數據佔領頭部數據,因此須要改進。Redis 每次按 key 獲取一個值的時候,都會更新 value 中的 lru 字段爲當前秒級別的時間戳。Redis 初始的實現算法很簡單,隨機從 dict 中取出五個 key,淘汰一個 lru 字段值最小的。在 3.0 的時候,又改進了一版算法,首先第一次隨機選取的 key 都會放入一個 pool 中(pool 的大小爲 16),pool 中的 key 是按 lru 大小順序排列的。接下來每次隨機選取的 keylru 值必須小於 pool 中最小的 lru 纔會繼續放入,直到將 pool 放滿。放滿以後,每次若是有新的 key 須要放入,須要將 pool 中 lru 最大的一個 key 取出。淘汰的時候,直接從 pool 中選取一個 lru 最小的值而後將其淘汰。
憑藉經驗,進行預估:例如提早知道了某個活動的開啓,那麼就將此 Key 做爲熱點 Key。
服務端收集:在操做 redis 以前,加入一行代碼進行數據統計。
抓包進行評估:Redis 使用 TCP 協議與客戶端進行通訊,通訊協議採用的是 RESP,因此本身寫程序監聽端口也能進行攔截包進行解析。
在 proxy 層,對每個 redis 請求進行收集上報。
Redis 自帶命令查詢:Redis4.0.4 版本提供了 redis-cli –hotkeys 就能找出熱點 Key。(若是要用 Redis 自帶命令查詢時,要注意須要先把內存逐出策略設置爲 allkeys-lfu 或者 volatile-lfu,不然會返回錯誤。進入 Redis 中使用 config set maxmemory-policy allkeys-lfu 便可。)
服務端緩存:即將熱點數據緩存至服務端的內存中.(利用 Redis 自帶的消息通知機制來保證 Redis 和服務端熱點 Key 的數據一致性,對於熱點 Key 客戶端創建一個監聽,當熱點 Key 有更新操做的時候,服務端也隨之更新。)
備份熱點 Key:即將熱點 Key+隨機數,隨機分配至 Redis 其餘節點中。這樣訪問熱點 key 的時候就不會所有命中到一臺機器上了。
使用 Redis 高可用架構:使用 Redis 集羣來保證 Redis 服務不會掛掉
緩存時間不一致,給緩存的失效時間,加上一個隨機值,避免集體失效
限流降級策略:有必定的備案,好比個性推薦服務不可用了,換成熱點數據推薦服務
在接口作校驗
存 null 值(緩存擊穿加鎖,或設置不過時)
布隆過濾器攔截: 將全部可能的查詢 key 先映射到布隆過濾器中,查詢時先判斷 key 是否存在布隆過濾器中,存在才繼續向下執行,若是不存在,則直接返回。布隆過濾器將值進行屢次哈希 bit 存儲,布隆過濾器說某個元素在,可能會被誤判。布隆過濾器說某個元素不在,那麼必定不在。
Redis 爲了保證效率,數據緩存在了內存中,可是會週期性地把更新的數據寫入磁盤或者把修改操做寫入追加的記錄文件中,以保證數據的持久化。Redis 的持久化策略有兩種:
RDB:快照形式是直接把內存中的數據保存到一個 dump 的文件中,定時保存,保存策略。當 Redis 須要作持久化時,Redis 會 fork 一個子進程,子進程將數據寫到磁盤上一個臨時 RDB 文件中。當子進程完成寫臨時文件後,將原來的 RDB 替換掉。
AOF:把全部的對 Redis 的服務器進行修改的命令都存到一個文件裏,命令的集合。
使用 AOF 作持久化,每個寫命令都經過 write 函數追加到 appendonly.aof 中。aof 的默認策略是每秒鐘 fsync 一次,在這種配置下,就算髮生故障停機,也最多丟失一秒鐘的數據。 缺點是對於相同的數據集來講,AOF 的文件體積一般要大於 RDB 文件的體積。根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB。 Redis 默認是快照 RDB 的持久化方式。對於主從同步來講,主從剛剛鏈接的時候,進行全量同步(RDB);全同步結束後,進行增量同步(AOF)。
Redis 事務的本質是一組命令的集合。事務支持一次執行多個命令,一個事務中全部命令都會被序列化。在事務執行過程,會按照順序串行化執行隊列中的命令,其餘客戶端提交的命令請求不會插入到事務執行命令序列中。總結說:redis 事務就是一次性、順序性、排他性的執行一個隊列中的一系列命令。
Redis 事務沒有隔離級別的概念,批量操做在發送 EXEC 命令前被放入隊列緩存,並不會被實際執行,也就不存在事務內的查詢要看到事務裏的更新,事務外查詢不能看到。
Redis 中,單條命令是原子性執行的,但事務不保證原子性,且沒有回滾。事務中任意命令執行失敗,其他的命令仍會被執行。
watch key1 key2 ... : 監視一或多個 key,若是在事務執行以前,被監視的 key 被其餘命令改動,則事務被打斷(相似樂觀鎖)
multi : 標記一個事務塊的開始(queued)
exec : 執行全部事務塊的命令(一旦執行 exec 後,以前加的監控鎖都會被取消掉)
discard : 取消事務,放棄事務塊中的全部命令
unwatch : 取消 watch 對全部 key 的監控
存儲方式上:memcache 會把數據所有存在內存之中,斷電後會掛掉,數據不能超過內存大小。redis 有部分數據存在硬盤上,這樣能保證數據的持久性。
數據支持類型上:memcache 對數據類型的支持簡單,只支持簡單的 key-value,,而 redis 支持五種數據類型。
用底層模型不一樣:它們之間底層實現方式以及與客戶端之間通訊的應用協議不同。redis 直接本身構建了 VM 機制,由於通常的系統調用系統函數的話,會浪費必定的時間去移動和請求。
value 的大小:redis 能夠達到 1GB,而 memcache 只有 1MB。
主從複製
哨兵模式
cluster 模式
哨兵是一個分佈式系統,在主從複製的基礎上你能夠在一個架構中運行多個哨兵進程,這些進程使用流言協議來接收關於 Master 是否下線的信息,並使用投票協議來決定是否執行自動故障遷移,以及選擇哪一個 Slave 做爲新的 Master。
每一個哨兵會向其它哨兵、master、slave 定時發送消息,以確認對方是否活着,若是發現對方在指定時間(可配置)內未迴應,則暫時認爲對方已掛(所謂的」主觀認爲宕機」)。
若「哨兵羣「中的多數 sentinel,都報告某一 master 沒響應,系統才認爲該 master"完全死亡"(即:客觀上的真正 down 機),經過必定的 vote 算法,從剩下的 slave 節點中,選一臺提高爲 master,而後自動修改相關配置。
Redis 的 rehash 操做並非一次性、集中式完成的,而是分屢次、漸進式地完成的,redis 會維護維持一個索引計數器變量 rehashidx 來表示 rehash 的進度。
這種漸進式的 rehash 避免了集中式 rehash 帶來的龐大計算量和內存操做,可是須要注意的是 redis 在進行 rehash 的時候,正常的訪問請求可能須要作多要訪問兩次 hashtable(ht[0], ht[1]),例如鍵值被 rehash 到新 ht1,則須要先訪問 ht0,若是 ht0 中找不到,則去 ht1 中找。
哈希表中保存的 key 數量超過了哈希表的大小.
Redis 服務器目前沒有在執行 BGSAVE 命令(rdb)或 BGREWRITEAOF 命令,而且哈希表的負載因子大於等於 1.
Redis 服務器目前在執行 BGSAVE 命令(rdb)或 BGREWRITEAOF 命令,而且哈希表的負載因子大於等於 5.(負載因子=哈希表已保存節點數量 / 哈希表大小,當哈希表的負載因子小於 0.1 時,對哈希表執行收縮操做。)
分佈式鎖+時間戳
利用消息隊列
先更新數據庫,再刪緩存。數據庫的讀操做的速度遠快於寫操做的,因此髒數據很難出現。能夠對異步延時刪除策略,保證讀請求完成之後,再進行刪除操做。
對於單線程阻塞式的 Redis,Pipeline 能夠知足批量的操做,把多個命令連續的發送給 Redis Server,而後一一解析響應結果。Pipelining 能夠提升批量處理性能,提高的緣由主要是 TCP 鏈接中減小了「交互往返」的時間。pipeline 底層是經過把全部的操做封裝成流,redis 有定義本身的出入輸出流。在 sync() 方法執行操做,每次請求放在隊列裏面,解析響應包。
本人十年後端研發經驗,任職架構師,曾「混跡」多個互聯網大廠,專一軟件架構技術研究學習,但願可以不斷沉澱、學習以及分享,將本身工做中的問題和技術總結輸出,分享影響到更多的人;
公衆號專一:軟件架構研究,技術學習與職業成長。內容涵蓋:系統架構應用匯總、消息中間件、MySQL 實用探祕、職業認知升級 四大模塊,你們能夠在公衆號底部菜單「精選專題」裏隨時查閱;
你們看個人公衆號頭像圖片像是一個陀螺,實際上是寓意螺旋式上升,讓技術和自我可以不斷精進。
文章首發於我的同名公衆號《架構精進之路》,原文連接:2021年最新Redis面試題彙總,值得收藏
Thanks for reading!