場景:Redis面試
(圖片來源於網絡)html
面試官: 我看到你的簡歷上說你熟練使用Redis,那麼你講一下Redis是幹嗎用的?git
小明: (心中竊喜,Redis不就是緩存嗎?)Redis主要用做緩存,經過內存高效地存儲非持久化數據。github
面試官: Redis能夠用做持久化的存儲嗎?面試
小明 :嗯...應該能夠吧...數據庫
面試官: 那Redis怎麼進行持久化操做呢?後端
小明:嗯...不是太清楚。緩存
面試官: Redis的內存淘汰機制有哪些?網絡
小明:嗯...沒了解過數據結構
面試官:咱們還能夠用Redis作哪些事情?分別利用了Redis的哪一個指令?多線程
小明:我只知道Redis還能夠作分佈式鎖、消息隊列...
面試官:好了,咱們進入下一個話題...
思考:很明顯,小明同窗在面試過程當中關於Redis的表現和回答確定是比較失敗的。Redis是咱們工做中天天都會使用到的東西,爲何一到面試卻變成了丟分項呢?
做爲開發者,咱們習慣了使用大神們已經封裝好的東西,以此保障咱們可以更專一於業務開發,殊不知道這些經常使用工具的底層實現是什麼,所以儘管平時應用起來駕輕就熟,但一到面試仍是沒法讓面試官眼前一亮。
本文總結了一些Redis的知識點,有原理有應用,但願能夠幫助到你們。
1、Redis是什麼
REmote DIctionary Server(Redis) 是一個由Salvatore Sanfilippo寫的key-value存儲系統。
Redis是一個開源的使用ANSI 、C語言編寫、遵照BSD協議、支持網絡、可基於內存亦可持久化的日誌型、Key-Value數據庫,並提供多種語言的API。
這裏我引用了Redis教程裏對Redis的描述,很官方,可是很標準。 可基於內存亦可持久化的日誌型、Key-Value數據庫。 我認爲這個描述很貼切很全面。
1.1 Redis的行業地位
Redis是互聯網技術領域使用最爲普遍的存儲中間件,因超高的性能、完美的文檔、多方面的應用能力以及豐富完善的客戶端支持在存儲方面獨當一面,廣受好評,尤爲以其性能和讀取速度而成爲了領域中最受青睞的中間件。基本上每個軟件公司都會使用Redis,其中包括不少大型互聯網公司,好比京東、阿里、騰訊、github等。所以,Redis也成爲了後端開發人員必不可少的技能。
1.2 知識圖譜
在我看來,學習每一項技術,都須要有一個清晰的脈絡和結構,否則你也不知道本身會了哪些、還有多少沒學會。就像一本書,若是沒有目錄章節,也就失去了靈魂。
所以我試圖總結出Redis的知識圖譜,也稱爲腦圖,以下圖所示,可能知識點不是很全,後續會不斷更新補充。
本系列文章的知識點也會和這個腦圖基本一致,本文先介紹Redis的基本知識,後續文章會詳細介紹Redis的數據結構、應用、持久化等多個方面。
2、Redis優勢
2.1 速度快
做爲緩存工具,Redis最廣爲人知的特色就是快,到底有多快呢?Redis單機qps(每秒的併發)能夠達到110000次/s,寫的速度是81000次/s。 那麼,Redis爲何這麼快呢?
- 絕大部分請求是純粹的內存操做,很是快速;
- 使用了不少查找操做都特別快的數據結構進行數據存儲,Redis中的數據結構是專門設計的。如HashMap,查找、插入的時間複雜度都是O(1);
- 採用單線程,避免了沒必要要的上下文切換和競爭條件,也不存在多進程或者多線程致使的切換而消耗CPU,不用去考慮各類鎖的問題,不存在加鎖、釋放鎖操做,沒有由於可能出現死鎖而致使的性能消耗;
- 用到了非阻塞I/O多路複用機制。
2.2 豐富的數據類型
Redis有5種經常使用的數據類型:String、List、Hash、set、zset,每種數據類型都有本身的用處。
2.3 原子性,支持事務
Redis支持事務,而且它的全部操做都是原子性的,同時Redis還支持對幾個操做合併後的原子性執行。
2.4 豐富的特性
Redis具備豐富的特性,好比能夠用做分佈式鎖;能夠持久化數據;能夠用做消息隊列、排行榜、計數器;還支持publish/subscribe、通知、key過時等等。當咱們要用中間件來解決實際問題的時候,Redis總能發揮出本身的用處。
3、Redis和Memcache對比
Memcache和Redis都是優秀的、高性能的內存數據庫,通常咱們說到Redis的時候,都會拿Memcache來和Redis作對比。(爲何要作對比呢?固然是要陪襯出Redis有多好,沒有對比,就沒有傷害~)對比的方面包括:
3.1 存儲方式
-
Memcache把數據所有存在內存之中,斷電後會掛掉,沒法作到數據的持久化,且數據不能超過內存大小。
-
Redis有一部分數據存在硬盤上,能夠作到數據的持久性。
3.2 數據支持類型
-
Memcache對數據類型支持相對簡單,只支持String類型的數據結構。
-
Redis有豐富的數據類型,包括:String、List、Hash、Set、Zset。
3.3 使用的底層模型
-
它們之間底層實現方式以及與客戶端之間通訊的應用協議不同。
-
Redis直接本身構建了VM機制 ,由於通常的系統調用系統函數,會浪費必定的時間去移動和請求。
3.4 存儲值大小
- Redis最大能夠存儲1GB,而memcache只有1MB。
看到這裏,會不會以爲Redis特別好,全是優勢,天衣無縫?其實Redis仍是有不少缺點的,這些缺點日常咱們該如何克服呢?
4、Redis存在的問題及解決方案
4.1 緩存數據庫的雙寫一致性的問題
問題:一致性的問題是分佈式系統中很常見的問題。一致性通常分爲兩種:強一致性和最終一致性,當咱們要知足強一致性的時候,Redis也沒法作到完美無瑕,由於數據庫和緩存雙寫,確定會出現不一致的狀況,Redis只能保證最終一致性。
解決:咱們如何保證最終一致性呢?
-
第一種方式是給緩存設置必定的過時時間,在緩存過時以後會自動查詢數據庫,保證數據庫和緩存的一致性。
-
若是不設置過時時間的話,咱們首先要選取正確的更新策略:先更新數據庫再刪除緩存。但咱們刪除緩存的時候也可能出現某些問題,因此須要將要刪除的緩存的key放到消息隊列中去,不斷重試,直到刪除成功爲止。
4.2 緩存雪崩問題
問題: 咱們應該都在電影裏看到過雪崩,開始很平靜,而後一瞬間就開始崩塌,具備很強的毀滅性。這裏也是同樣的,咱們執行代碼的時候將不少緩存的實效時間設定成同樣,接着這些緩存在同一時間都會實效,而後都會從新訪問數據庫更新數據,這樣會致使數據庫鏈接數過多、壓力過大而崩潰。
解決:
- 設置緩存過時時間的時候加一個隨機值。
- 設置雙緩存,緩存1設置緩存時間,緩存2不設置,1過時後直接返回緩存2,而且啓動一個進程去更新緩存1和2。
4.3 緩存穿透問題
問題: 緩存穿透是指一些非正經常使用戶(黑客)故意去請求緩存中不存在的數據,致使全部的請求都集中到到數據庫上,從而致使數據庫鏈接異常。
解決:
-
利用互斥鎖。緩存失效的時候,不能直接訪問數據庫,而是要先獲取到鎖,才能去請求數據庫。沒獲得鎖,則休眠一段時間後重試。
-
採用異步更新策略。不管key是否取到值,都直接返回。value值中維護一個緩存失效時間,緩存若是過時,異步起一個線程去讀數據庫,更新緩存。須要作緩存預熱(項目啓動前,先加載緩存)操做。
-
提供一個能迅速判斷請求是否有效的攔截機制。好比利用布隆過濾器,內部維護一系列合法有效的key,迅速判斷出請求所攜帶的Key是否合法有效。若是不合法,則直接返回。
4.4 緩存的併發競爭問題
問題:
緩存併發競爭的問題,主要發生在多線程對某個key進行set的時候,這時會出現數據不一致的狀況。
好比Redis中咱們存着一個key爲amount的值,它的value是100,兩個線程同時都對value加100而後更新,正確的結果應該是變爲300。可是兩個線程拿到這個值的時候都是100,最後結果也就是200,這就致使了緩存的併發競爭問題。
解決
- 若是多線程操做沒有順序要求的話,咱們能夠設置一個分佈式鎖,而後多個線程去爭奪鎖,誰先搶到鎖誰就能夠先執行。這個分佈式鎖能夠用zookeeper或者Redis自己去實現。
- 能夠利用Redis的incr命令。
- 當咱們的多線程操做須要順序的時候,咱們能夠設置一個消息隊列,把須要的操做加到消息隊列中去,嚴格按照隊列的前後執行命令。
5、Redis的過時策略
Redis隨着數據的增多,內存佔用率會持續變高,咱們覺得一些鍵到達設置的刪除時間就會被刪除,可是時間到了,內存的佔用率仍是很高,這是爲何呢?
Redis採用的是按期刪除和惰性刪除的內存淘汰機制。
5.1 按期刪除
按期刪除和定時刪除是有區別的:
-
定時刪除是必須嚴格按照設定的時間去刪除緩存,這就須要咱們設置一個定時器去不斷地輪詢全部的key,判斷是否須要進行刪除。可是這樣的話cpu的資源會被大幅度地佔據,資源的利用率變低。因此咱們選擇採用按期刪除,。
-
按期刪除是時間由咱們定,咱們能夠每隔100ms進行檢查,但仍是不能檢查全部的緩存,Redis仍是會卡死,只能隨機地去檢查一部分緩存,可是這樣會有一些緩存沒法在規定時間內刪除。這時惰性刪除就派上用場了。
5.2 惰性刪除
舉個簡單的例子:中學的時候,平時做業太多,根本作不完,老師說下節課要講這個卷子,大家都作完了吧?其實有不少人沒作完,因此須要在下節課以前趕忙補上。
惰性刪除也是這個道理,咱們的這個值按理說應該沒了,可是它還在,當你要獲取這個key的時候,發現這個key應該過時了,趕忙刪了,而後返回一個'沒有這個值,已通過期了!'。
如今咱們有了按期刪除 + 惰性刪除的過時策略,就能夠高枕無憂了嗎?並非這樣的,若是這個key一直不訪問,那麼它會一直滯留,也是不合理的,這就須要咱們的內存淘汰機制了。
5.3 Redis的內存淘汰機制
Redis的內存淘汰機制通常有6種,以下圖所示:
那麼咱們如何去配置Redis的內存淘汰機制呢?
在Redis.conf中咱們能夠進行配置
# maxmemory-policy allkeys-lru
6、小結
本文初探Redis,大概整理出了Redis的知識圖譜,對照之下能夠發現Redis竟然有這麼多的知識點須要學習;接着咱們分析了Redis的優缺點,知道了其基於內存的高效的讀寫速度和豐富的數據類型,也分析了Redis面對數據一致性、緩存穿透、緩存雪崩等問題時該如何處理;最後咱們瞭解了Redis的過時策略和緩存淘汰機制。
相信你們已經對Redis有了一些瞭解,下篇文章咱們將分析Redis的數據結構、每一種數據類型是如何實現的、對應的命令有哪些。