絕大部分寫業務的程序員,在實際開發中使用 Redis 的時候,只會 Set Value 和 Get Value 兩個操做,對 Redis 總體缺少一個認知。這裏以面試題的形式對 Redis 常見問題作一個總結,解決你們的知識盲點。程序員
在項目中使用 Redis,主要考慮兩個角度:性能和併發。若是隻是爲了分佈式鎖這些其餘功能,還有其餘中間件 Zookpeer 等代替,並不是必定要使用 Redis。面試
性能:redis
以下圖所示,咱們在碰到須要執行耗時特別久,且結果不頻繁變更的 SQL,就特別適合將運行結果放入緩存。這樣,後面的請求就去緩存中讀取,使得請求可以迅速響應。算法
特別是在秒殺系統,在同一時間,幾乎全部人都在點,都在下單。。。執行的是同一操做———向數據庫查數據。數據庫
根據交互效果的不一樣,響應時間沒有固定標準。在理想狀態下,咱們的頁面跳轉須要在瞬間解決,對於頁內操做則須要在剎那間解決。緩存
併發:數據結構
以下圖所示,在大併發的狀況下,全部的請求直接訪問數據庫,數據庫會出現鏈接異常。這個時候,就須要使用 Redis 作一個緩衝操做,讓請求先訪問到 Redis,而不是直接訪問數據庫。併發
使用 Redis 的常見問題運維
這個問題是對 Redis 內部機制的一個考察。不少人都不知道 Redis 是單線程工做模型。dom
緣由主要是如下三點:
仔細說一說 I/O 多路複用機制,打一個比方:小名在 A 城開了一家快餐店店,負責同城快餐服務。小明由於資金限制,僱傭了一批配送員,而後小曲發現資金不夠了,只夠買一輛車送快遞。
經營方式一
客戶每下一份訂單,小明就讓一個配送員盯着,而後讓人開車去送。慢慢的小曲就發現了這種經營方式存在下述問題:
綜合上述缺點,小明痛定思痛,提出了經營方式二。
經營方式二
小明只僱傭一個配送員。當客戶下單,小明按送達地點標註好,依次放在一個地方。最後,讓配送員依次開着車去送,送好了就回來拿下一個。上述兩種經營方式對比,很明顯第二種效率更高。
在上述比喻中:
因而有了以下結論:
下面類比到真實的 Redis 線程模型,如圖所示:
-
Redis-client 在操做的時候,會產生具備不一樣事件類型的 Socket。在服務端,有一段 I/O 多路複用程序,將其置入隊列之中。而後,文件事件分派器,依次去隊列中取,轉發到不一樣的事件處理器中。
一個合格的程序員,這五種類型都會用到。
String
最常規的 set/get 操做,Value 能夠是 String 也能夠是數字。通常作一些複雜的計數功能的緩存。
Hash
這裏 Value 存放的是結構化的對象,比較方便的就是操做其中的某個字段。我在作單點登陸的時候,就是用這種數據結構存儲用戶信息,以 CookieId 做爲 Key,設置 30 分鐘爲緩存過時時間,能很好的模擬出相似 Session 的效果。
List
使用 List 的數據結構,能夠作簡單的消息隊列的功能。另外,能夠利用 lrange 命令,作基於 Redis 的分頁功能,性能極佳,用戶體驗好。
Set
由於 Set 堆放的是一堆不重複值的集合。因此能夠作全局去重的功能。咱們的系統通常都是集羣部署,使用 JVM 自帶的 Set 比較麻煩。另外,就是利用交集、並集、差集等操做,能夠計算共同喜愛,所有的喜愛,本身獨有的喜愛等功能。
Sorted Set
Sorted Set 多了一個權重參數 Score,集合中的元素可以按 Score 進行排列。能夠作排行榜應用,取 TOP N 操做。Sorted Set 能夠用來作延時任務。
Redis 是否用到家,從這就能看出來。好比你 Redis 只能存 5G 數據,但是你寫了 10G,那會刪 5G 的數據。怎麼刪的,這個問題思考過麼?
正解:Redis 採用的是按期刪除+惰性刪除策略。
爲何不用定時刪除策略
定時刪除,用一個定時器來負責監視 Key,過時則自動刪除。雖然內存及時釋放,可是十分消耗 CPU 資源。在大併發請求下,CPU 要將時間應用在處理請求,而不是刪除 Key,所以沒有采用這一策略。
按期刪除+惰性刪除如何工做
按期刪除,Redis 默認每一個 100ms 檢查,有過時 Key 則刪除。須要說明的是,Redis 不是每一個 100ms 將全部的 Key 檢查一次,而是隨機抽取進行檢查。若是隻採用按期刪除策略,會致使不少 Key 到時間沒有刪除。因而,惰性刪除派上用場。
採用按期刪除+惰性刪除就沒其餘問題了麼
不是的,若是按期刪除沒刪除掉 Key。而且你也沒及時去請求 Key,也就是說惰性刪除也沒生效。這樣,Redis 的內存會愈來愈高。那麼就應該採用內存淘汰機制。
在 redis.conf 中有一行配置:
# maxmemory-policy volatile-lru
該配置就是配內存淘汰策略的:
一致性問題還能夠再分爲最終一致性和強一致性。數據庫和緩存雙寫,就必然會存在不一致的問題。前提是若是對數據有強一致性要求,不能放緩存。咱們所作的一切,只能保證最終一致性。
另外,咱們所作的方案從根本上來講,只能下降不一致發生的機率。所以,有強一致性要求的數據,不能放緩存。首先,採起正確更新策略,先更新數據庫,再刪緩存。其次,由於可能存在刪除緩存失敗的問題,提供一個補償措施便可,例如利用消息隊列。
這兩個問題,通常中小型傳統軟件企業很難碰到。若是有大併發的項目,流量有幾百萬左右,這兩個問題必定要深入考慮。緩存穿透,即黑客故意去請求緩存中不存在的數據,致使全部的請求都懟到數據庫上,從而數據庫鏈接異常。
緩存穿透解決方案:
緩存雪崩,即緩存同一時間大面積的失效,這個時候又來了一波請求,結果請求都懟到數據庫上,從而致使數據庫鏈接異常。
緩存雪崩解決方案:
這個問題大體就是,同時有多個子系統去 Set 一個 Key。這個時候要注意什麼呢?你們基本都是推薦用 Redis 事務機制。
可是我並不推薦使用 Redis 的事務機制。由於咱們的生產環境,基本都是 Redis 集羣環境,作了數據分片操做。你一個事務中有涉及到多個 Key 操做的時候,這多個 Key 不必定都存儲在同一個 redis-server 上。所以,Redis 的事務機制,十分雞肋。
若是對這個 Key 操做,不要求順序
這種狀況下,準備一個分佈式鎖,你們去搶鎖,搶到鎖就作 set 操做便可,比較簡單。
若是對這個 Key 操做,要求順序
假設有一個 key1,系統 A 須要將 key1 設置爲 valueA,系統 B 須要將 key1 設置爲 valueB,系統 C 須要將 key1 設置爲 valueC。
指望按照 key1 的 value 值按照 valueA > valueB > valueC 的順序變化。這種時候咱們在數據寫入數據庫的時候,須要保存一個時間戳。
假設時間戳以下:
系統 A key 1 {valueA 3:00}
系統 B key 1 {valueB 3:05}
系統 C key 1 {valueC 3:10}
那麼,假設系統 B 先搶到鎖,將 key1 設置爲{valueB 3:05}。接下來系統 A 搶到鎖,發現本身的 valueA 的時間戳早於緩存中的時間戳,那就不作 set 操做了,以此類推。其餘方法,好比利用隊列,將 set 方法變成串行訪問也能夠。
Redis 在國內各大公司都能看到其身影,好比咱們熟悉的新浪,阿里,騰訊,百度,美團,小米等。學習 Redis,這幾方面尤爲重要:Redis 客戶端、Redis 高級功能、Redis 持久化和開發運維經常使用問題探討、Redis 複製的原理和優化策略、Redis 分佈式解決方案等。
若是你以爲文章對你有幫助的話,能夠關注、點贊、收藏、轉發走一波,謝謝!