'無論面試還是工作,關於Redis理解這些就夠了'

"

Redis是經常被用到的緩存中間件,也是面試過程中常被問到的。這篇文章總結了相關的技術點。持續分享互聯網開發技術乾貨,歡迎關注我。

一、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是什麼

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的基本知識,後續文章會詳細介紹Redis的數據結構、應用、持久化等多個方面。

二、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總能發揮出自己的用處。

三、Redis和Memcache對比

Memcache和Redis都是優秀的、高性能的內存數據庫,一般我們說到Redis的時候,都會拿Memcache來和Redis做對比。(為什麼要做對比呢?當然是要襯托出Redis有多好,沒有對比,就沒有傷害~)對比的方面包括:

  1. 存儲方式
  • Memcache把數據全部存在內存之中,斷電後會掛掉,無法做到數據的持久化,且數據不能超過內存大小。
  • Redis有一部分數據存在硬盤上,可以做到數據的持久性。
  1. 數據支持類型
  • Memcache對數據類型支持相對簡單,只支持String類型的數據結構。
  • Redis有豐富的數據類型,包括:String、List、Hash、Set、Zset。
  1. 使用的底層模型
  • 它們之間底層實現方式以及與客戶端之間通信的應用協議不一樣。
  • Redis直接自己構建了VM機制 ,因為一般的系統調用系統函數,會浪費一定的時間去移動和請求。

4) 存儲值大小

  • Redis最大可以存儲1GB,而memcache只有1MB。

看到這裡,會不會覺得Redis特別好,全是優點,完美無缺?其實Redis還是有很多缺點的,這些缺點平常我們該如何克服呢?

四、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命令。
  • 當我們的多線程操作需要順序的時候,我們可以設置一個消息隊列,把需要的操作加到消息隊列中去,嚴格按照隊列的先後執行命令。

五、Redis的過期策略

Redis隨著數據的增多,內存佔用率會持續變高,我們以為一些鍵到達設置的刪除時間就會被刪除,但是時間到了,內存的佔用率還是很高,這是為什麼呢?

Redis採用的是定期刪除惰性刪除的內存淘汰機制。

5.1 定期刪除

定期刪除和定時刪除是有區別的:

  • 定時刪除是必須嚴格按照設定的時間去刪除緩存,這就需要我們設置一個定時器去不斷地輪詢所有的key,判斷是否需要進行刪除。但是這樣的話cpu的資源會被大幅度地佔據,資源的利用率變低。所以我們選擇採用定期刪除,。
  • 定期刪除是時間由我們定,我們可以每隔100ms進行檢查,但還是不能檢查所有的緩存,Redis還是會卡死,只能隨機地去檢查一部分緩存,但是這樣會有一些緩存無法在規定時間內刪除。這時惰性刪除就派上用場了。

5.2 惰性刪除

舉個簡單的例子:中學的時候,平時作業太多,根本做不完,老師說下節課要講這個卷子,你們都做完了吧?其實有很多人沒做完,所以需要在下節課之前趕緊補上。

惰性刪除也是這個道理,我們的這個值按理說應該沒了,但是它還在,當你要獲取這個key的時候,發現這個key應該過期了,趕緊刪了,然後返回一個'沒有這個值,已經過期了!'。

現在我們有了定期刪除 + 惰性刪除的過期策略,就可以高枕無憂了嗎?並不是這樣的,如果這個key一直不訪問,那麼它會一直滯留,也是不合理的,這就需要我們的內存淘汰機制了。

5.3 Redis的內存淘汰機制

Redis的內存淘汰機制一般有6種,如下圖所示:

"

Redis是經常被用到的緩存中間件,也是面試過程中常被問到的。這篇文章總結了相關的技術點。持續分享互聯網開發技術乾貨,歡迎關注我。

一、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的基本知識,後續文章會詳細介紹Redis的數據結構、應用、持久化等多個方面。

二、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總能發揮出自己的用處。

三、Redis和Memcache對比

Memcache和Redis都是優秀的、高性能的內存數據庫,一般我們說到Redis的時候,都會拿Memcache來和Redis做對比。(為什麼要做對比呢?當然是要襯托出Redis有多好,沒有對比,就沒有傷害~)對比的方面包括:

  1. 存儲方式
  • Memcache把數據全部存在內存之中,斷電後會掛掉,無法做到數據的持久化,且數據不能超過內存大小。
  • Redis有一部分數據存在硬盤上,可以做到數據的持久性。
  1. 數據支持類型
  • Memcache對數據類型支持相對簡單,只支持String類型的數據結構。
  • Redis有豐富的數據類型,包括:String、List、Hash、Set、Zset。
  1. 使用的底層模型
  • 它們之間底層實現方式以及與客戶端之間通信的應用協議不一樣。
  • Redis直接自己構建了VM機制 ,因為一般的系統調用系統函數,會浪費一定的時間去移動和請求。

4) 存儲值大小

  • Redis最大可以存儲1GB,而memcache只有1MB。

看到這裡,會不會覺得Redis特別好,全是優點,完美無缺?其實Redis還是有很多缺點的,這些缺點平常我們該如何克服呢?

四、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命令。
  • 當我們的多線程操作需要順序的時候,我們可以設置一個消息隊列,把需要的操作加到消息隊列中去,嚴格按照隊列的先後執行命令。

五、Redis的過期策略

Redis隨著數據的增多,內存佔用率會持續變高,我們以為一些鍵到達設置的刪除時間就會被刪除,但是時間到了,內存的佔用率還是很高,這是為什麼呢?

Redis採用的是定期刪除惰性刪除的內存淘汰機制。

5.1 定期刪除

定期刪除和定時刪除是有區別的:

  • 定時刪除是必須嚴格按照設定的時間去刪除緩存,這就需要我們設置一個定時器去不斷地輪詢所有的key,判斷是否需要進行刪除。但是這樣的話cpu的資源會被大幅度地佔據,資源的利用率變低。所以我們選擇採用定期刪除,。
  • 定期刪除是時間由我們定,我們可以每隔100ms進行檢查,但還是不能檢查所有的緩存,Redis還是會卡死,只能隨機地去檢查一部分緩存,但是這樣會有一些緩存無法在規定時間內刪除。這時惰性刪除就派上用場了。

5.2 惰性刪除

舉個簡單的例子:中學的時候,平時作業太多,根本做不完,老師說下節課要講這個卷子,你們都做完了吧?其實有很多人沒做完,所以需要在下節課之前趕緊補上。

惰性刪除也是這個道理,我們的這個值按理說應該沒了,但是它還在,當你要獲取這個key的時候,發現這個key應該過期了,趕緊刪了,然後返回一個'沒有這個值,已經過期了!'。

現在我們有了定期刪除 + 惰性刪除的過期策略,就可以高枕無憂了嗎?並不是這樣的,如果這個key一直不訪問,那麼它會一直滯留,也是不合理的,這就需要我們的內存淘汰機制了。

5.3 Redis的內存淘汰機制

Redis的內存淘汰機制一般有6種,如下圖所示:

無論面試還是工作,關於Redis理解這些就夠了

那麼我們如何去配置Redis的內存淘汰機制呢?

在Redis.conf中我們可以進行配置

# maxmemory-policy allkeys-lru

六、小結

本文初探Redis,大概整理出了Redis的知識圖譜,對照之下可以發現Redis居然有這麼多的知識點需要學習;接著我們分析了Redis的優缺點,知道了其基於內存的高效的讀寫速度和豐富的數據類型,也分析了Redis面對數據一致性、緩存穿透、緩存雪崩等問題時該如何處理;最後我們瞭解了Redis的過期策略和緩存淘汰機制。

相信大家已經對Redis有了一些瞭解,下篇文章我們將分析Redis的數據結構、每一種數據類型是如何實現的、對應的命令有哪些。

轉自:https://my.oschina.net/u/4007037/blog/3045328

"

相關推薦

推薦中...