「持續更新中,歡迎關注...」redis
1. 什麼是Redis
Redis 是互聯網技術領域使用最爲普遍的存儲中間件,它是Remote Dictionary Service的首字母縮寫,也就是遠程字典服務。Redis 以其超高的性能、完美的文檔、簡潔易懂的源碼和豐富的客戶端庫支持在開源中間件領域廣受好評。對 Redis 的瞭解和應用實踐已成爲當下中高級後端開發者繞不開的必備技能。算法
2. Redis能夠作什麼
- 數據緩存
- 消息隊列
- 分佈式鎖
- 會話緩存
- 時效性數據
- 訪問頻率
- 計數器
- 社交列表
- 記錄用戶斷定信息
- 交集、並集和差集
- 熱門列表與排行榜
- 最新動態
- 大數據去重
3. Redis的優勢
- 速度快:內存存儲,查找和操做的時間複雜度O(1)。
- 支持豐富的數據類型:提供了String、List、Hash、Set、Sorted Set 5種基礎數據結構,並擴展了Bitmap、HyperLogLog、GEO等高級數據結構。
- 豐富的特性:訂閱發佈Pub/Sub、Key過時策略、事務、計數、Stream等。
- 持久化存儲:提供了RDB和AOF兩種數據的持久化存儲方案,解決宕機數據丟失的問題。
- 高可用:提供Redis Sentinel高可用方案和Redis Cluster集羣方案。
4. Redis和Memcached區別
- 數據結構對比:Redis支持更復雜的數據結構,能支持更豐富的數據操做;Memcached 僅提供簡單的字符串。
- 性能對比:Redis 只使用單核(Redis 6開始支持多核),而 Memcached 可使用多核,因此平均每個核上 Redis在存儲小數據時比 Memcached 性能更高。
- 持久化對比:Redis 支持數據持久化和數據恢復,容許單點故障,可是同時也會付出性能的代價。Memcached不支持數據持久化,斷電或重啓後數據消失,但其穩定性是有保證的。
- 集羣對比:Redis3.x+ 版本中,官方便能支持 Cluster 集羣模式。Memcached 沒有原生的集羣模式,須要依靠客戶端來實現往集羣中分片寫入數據。Memcached的分佈式不是在服務器端實現的,而是在客戶端應用中實現的,即經過內置算法制定目標數據的節點。
- 內存利用率對比:簡單的 Key-Value 存儲的話,Memcached 的內存利用率更高,可使用相似內存池。
若是 Redis 採用 hash 結構來作 key-value 存儲,因爲其組合式的壓縮, 其內存利用率會高於 Memcached 。數據庫
- 內存管理對比:Redis 採用的是包裝的 malloc/free ,相較於 Memcached 的內存管理方法 tcmalloc / jmalloc 來講,要簡單不少。
- 網絡IO模型對比:Memcached 是多線程,非阻塞 IO 複用的網絡模型,原型上接近 Nignx 。Redis 使用單線程的 IO 複用模型,本身封裝了一個簡單的 AeEvent 事件處理框架,主要實現了 epoll, kqueue 和 select ,更接近 Apache 早期的模式。
5. 爲何 Redis 這麼快?
- C語言實現。
- 純內存操做。
- 核心是基於非阻塞的IO多路複用機制。
- 單線程反而避免了多線程的頻繁上下文切換問題。Redis 利用隊列技術,將併發訪問變爲串行訪問,消除了傳統數據庫串行控制的開銷。第一,單線程能夠簡化數據結構和算法的實現。第二,單線程避免了線程切換和競態產生的消耗,對於服務端開發來講,鎖和線程切換一般是性能殺手。
- Redis 使用 hash 結構,讀取速度快,還有一些特殊的數據結構,對數據存儲進行了優化,如壓縮表對短數據進行壓縮存儲;再如跳錶使用有序的數據結構加快讀取的速度。
6. Redis線程模型
關鍵字:非阻塞 IO、多路複用。後端
redis 內部使用文件事件處理器 file event handler,這個文件事件處理器是單線程的,因此 redis 才叫作單線程的模型。它採用 IO 多路複用機制同時監聽多個 socket,根據 socket 上的事件來選擇對應的事件處理器進行處理。
文件事件處理器的結構包含 4 個部分:數組
- 多個 socket
- IO多路複用程序
- 文件事件分派器
- 事件處理器(鏈接應答處理器、命令請求處理器、命令回覆處理器)
多個 socket 可能會併發產生不一樣的操做,每一個操做對應不一樣的文件事件,可是 IO 多路複用程序會監聽多個 socket,會將 socket 產生的事件放入隊列中排隊,事件分派器每次從隊列中取出一個事件,把該事件交給對應的事件處理器進行處理。緩存
- 客戶端 socket01 向 redis 的 server socket 請求創建鏈接,此時 server socket 會產生一個 AE_READABLE 事件,IO 多路複用程序監聽到 server socket 產生的事件後,將該事件壓入隊列中。文件事件分派器從隊列中獲取該事件,交給鏈接應答處理器。鏈接應答處理器會建立一個能與客戶端通訊的 socket01,並將該 socket01 的 AE_READABLE 事件與命令請求處理器關聯。
- 假設此時客戶端發送了一個 set key value 請求,此時 redis 中的 socket01 會產生 AE_READABLE 事件,IO 多路複用程序將事件壓入隊列,此時事件分派器從隊列中獲取到該事件,因爲前面 socket01 的 AE_READABLE 事件已經與命令請求處理器關聯,所以事件分派器將事件交給命令請求處理器來處理。命令請求處理器讀取 socket01 的 key value 並在本身內存中完成 key value 的設置。操做完成後,它會將 socket01 的 AE_WRITABLE 事件與令回覆處理器關聯。
- 若是此時客戶端準備好接收返回結果了,那麼 redis 中的 socket01 會產生一個 AE_WRITABLE 事件,一樣壓入隊列中,事件分派器找到相關聯的命令回覆處理器,由命令回覆處理器對 socket01 輸入本次操做的一個結果,好比 ok,以後解除 socket01 的 AE_WRITABLE 事件與命令回覆處理器的關聯。
7. 如何提升Redis多核 CPU 的利用率
能夠在同一個服務器部署多個 Redis 的實例,並把他們看成不一樣的服務器來使用,在某些時候,不管如何一個服務器是不夠的,因此,若是想使用多個 CPU ,能夠考慮一下分區。
升級到Redis 6,能夠利用多核CPU。服務器
8. Redis數據結構及應用場景
Redis 全部的數據結構都是以惟一的 key 字符串做爲名稱,而後經過這個惟一 key 值來獲取相應的 value 數據。不一樣類型的數據結構的差別就在於 value 的結構不同。網絡
8.1 Redis 5種基礎數據結構:
- string(字符串):string表示的是一個可變的字節數組,Redis的字符串是動態字符串,是能夠修改的字符串,內部結構實現上相似於Java的ArrayList,採用預分配冗餘空間的方式來減小內存的頻繁分配。當字符串長度小於1M時,擴容都是加倍現有的空間,若是超過1M,擴容時一次只會多擴1M的空間。須要注意的是字符串最大長度爲512M。
- list (列表):list存儲結構用的是雙向鏈表而不是數組,隨機定位性能較弱( O(n)),首尾插入刪除性能較優(O(1))。Redis底層存儲是稱之爲快速鏈表quicklist的一個結構。首先在列表元素較少的狀況下會使用一塊連續的內存存儲,這個結構是ziplist,也便是壓縮列表。它將全部的元素緊挨着一塊兒存儲,分配的是一塊連續的內存。當數據量比較多的時候纔會改爲quicklist。由於普通的鏈表須要的附加指針空間太大,會比較浪費空間。好比這個列表裏存的只是int類型的數據,結構上還須要兩個額外的指針prev和next。因此Redis將鏈表和ziplist結合起來組成了quicklist。也就是將多個ziplist使用雙向指針串起來使用。這樣既知足了快速的插入刪除性能,又不會出現太大的空間冗餘。
- hash (哈希):哈希等價於Java語言的HashMap,在實現結構上它使用二維結構,第一維是數組,第二維是鏈表,hash的內容key和value存放在鏈表中,數組裏存放的是鏈表的頭指針。經過key查找元素時,先計算key的hashcode,而後用hashcode對數組的長度進行取模定位到鏈表的表頭,再對鏈表進行遍歷獲取到相應的value值,鏈表的做用就是用來將產生了「hash碰撞」的元素串起來。
- set (集合):set至關於 Java 語言裏面的 HashSet,使用hash結構,全部的value都指向同一個內部值。
當集合中最後一個元素移除以後,數據結構自動刪除,內存被回收。數據結構
- zset(Sorted Set) (有序集合):zset底層實現使用了兩個數據結構,第一個是hash,第二個是跳躍列表,hash的做用就是關聯元素value和權重score,保障元素value的惟一性,能夠經過元素value找到相應的score值。跳躍列表的目的在於給元素value排序,根據score的範圍獲取元素列表。
zset 中最後一個 value 被移除後,數據結構自動刪除,內存被回收。
zset 能夠用來存粉絲列表,value 值是粉絲的用戶 ID,score 是關注時間。咱們能夠對粉絲列表按關注時間進行排序。多線程
8.2 Redis 4種特殊數據結構:
- HyperLogLog
- Bitmaps
- GEO
- Stream
Redis Module
- BloomFilter
- RedisSearch
- Redis-ML
- JSON
Redis 5.0新增
9. Redis數據「過時」策略
在Redis中,同時使用了3種過時策略:
- 被動刪除:當讀/寫一個已通過期的 key 時,會觸發惰性刪除策略,直接刪除掉這個過時 key 。
- 主動刪除:因爲惰性刪除策略沒法保證冷數據被及時刪掉,因此 Redis 會按期主動淘汰一批已過時的 key 。
- 當前已用內存超過 maxmemory 限定時,觸發主動清理策略,即 「數據「淘汰」策略」 。
10. Redis數據「淘汰」策略
Redis 在生產環境中,採用配置參數 maxmemory 的方式來限制內存大小。當 Redis 內存超出物理內存限制時,內存數據就會與磁盤產生頻繁交換,使 Redis 性能急劇降低。所以如何淘汰無用數據釋放空間,存儲新數據變得尤其重要。
10.1 四種淘汰機制:
- LRU(Least recently used,最近最少使用):
- 根據數據的歷史訪問記錄來進行淘汰數據,其核心思想是「若是數據最近被訪問過,那麼未來被訪問的概率也更高」。
- 在服務器配置中保存了 lru 計數器 server.lrulock,會定時(redis 定時程序 serverCorn())更新,server.lrulock 的值是根據server.unixtime 計算出來進行排序的,而後選擇最近使用時間最久的數據進行刪除。每個 redis 對象都會設置相應的 lru。每一次訪問數據,會更新對應lru。
- LRU淘汰策略並非面向全部最久未使用的鍵值對,而只是隨機挑選的幾個鍵值對。
- TTL:
Redis 數據集數據結構中保存了鍵值對過時時間的表,TTL 數據淘汰機制中會先從過時時間的表中隨機挑選幾個鍵值對,取出其中 ttl 最大的鍵值對淘汰。TTL淘汰策略並非面向全部過時時間的表中最快過時的鍵值對,而只是隨機挑選的幾個鍵值對。
- RANDOM:
在隨機淘汰的場景下獲取待刪除的鍵值對,隨機找hash桶再次hash指定位置的dictEntry。
- LFU(Least Frequently Used,最久未使用)
- 根據數據的歷史訪問記錄來進行淘汰數據,其核心思想是「若是一個數據在最近一段時間內使用次數不多,那麼在未來一段時間內被使用的可能性也很小」。
10.2 8種淘汰策略及應用場景
Redis 提供了 8 種數據淘汰策略,其中volatile-lfu、allkeys-lfu 2種是Redis 4.0新增。
- volatile-lru
- 從已設置過時時間的數據集中挑選最近最少使用的數據淘汰。
- redis並非保證取得全部數據集中最近最少使用的鍵值對,而只是隨機挑選的幾個鍵值對中的,當內存達到限制的時候沒法寫入非過時時間的數據集。
- 沒有設置過時時間的key不會被淘汰,這樣就能夠在增長內存空間的同時保證須要持久化的數據不會丟失。
- volatile-ttl
- 從已設置過時時間的數據集中挑選將要過時的數據淘汰。
- redis 並非保證取得全部數據集中最近將要過時的鍵值對,而只是隨機挑選的幾個鍵值對中的, 當內存達到限制的時候沒法寫入非過時時間的數據集。
- ttl值越大越優先被淘汰。
- volatile-random
- 從已設置過時時間的數據集中任意選擇數據淘汰。
- 當內存達到限制沒法寫入非過時時間的數據集時。
- allkeys-lru
- 從數據集中挑選最近最少使用的數據淘汰。
- 當內存達到限制的時候,對全部數據集挑選最近最少使用的數據淘汰,可寫入新的數據集。
- 適用場景:若是咱們的應用對緩存的訪問符合冪律分佈,也就是存在相對熱點數據,或者咱們不太清楚咱們應用的緩存訪問分佈情況,咱們能夠選擇allkeys-lru策略。
- allkeys-random
- 從數據集中任意選擇數據淘汰。
- 當內存達到限制的時候,對全部數據集挑選隨機淘汰,可寫入新的數據集。
- 適用場景:若是咱們的應用對於緩存key的訪問機率相等,則可使用這個策略。
- no-enviction(默認策略)
- 當內存達到限制的時候,不淘汰任何數據,不可寫入任何數據集,全部引發申請內存的命令會報錯。
- volatile-lfu
- allkeys-lfu
10.3 Redis 回收進程如何工做的?
- 一個客戶端運行了新的寫命令,發起須要更多內存的申請。
- Redis 檢查內存使用狀況,若是大於 maxmemory 的限制, 則根據設定好的策略進行回收。
- Redis 執行新命令。