太牛了,值得收藏!7000字22张图,精讲 Redis 知识!
今天我們來分享 Redis 相關的知識,文章有點長,一定要看到最后呦!
前言
Redis 在當今的計算機行業,可以說是使用的最為廣泛的內存數據庫,幾乎所有的后端技術面試都會涉及到 Redis 相關的知識,正所謂知己知彼,百戰百勝。今天咱們就來盤一盤 Redis,從基礎面試題到各種特性、功能,做一次一網打盡式的服務!
什么是 Redis
Redis 是用 C 語言開發的一個開源的高性能鍵值對(key-value)數據庫。通常建議在 Linux 上運行,它通過提供多種鍵值數據類型來適應不同場景下的存儲需求,數據存儲在內存中,也可持久化到磁盤中,目前為止 Redis 支持的鍵值數據類型如下:
字符串類型
散列類型
列表類型
集合類型
有序集合類型
Redis 特色
Redis 是用 C 語言寫的開源項目,又由于數據都在內存中,所以讀寫速度非常快
Redis 所有數據保存在內存中,對數據的更新會異步地保存到磁盤上,這樣可以做到斷電不丟失數據
Redis 主從復制可以實現高可用和分布式
Redis 數據結構
字符串
String 是 Redis 當中最為基本的數據類型,最大512M,是二進制安全的
Redis 的字符串是動態字符串,是可以修改的,內部結構實現上類似于 Java 的 ArrayList,采用預分配冗余空間的方式來減少內存的頻繁分配,內存為當前字符串實際分配的空間,一般要高于實際字符串長度。當字符串長度小于 1M 時,擴容都是加倍現有的空間,如果超過 1M,擴容時一次只會多擴 1M 的空間
使用場景:記錄用戶頁面訪問量、緩存基本數據、分布式 Id 生成器
散列
Redis 當中的 Hash 相當于 Java 的 HashMap,無序字典,內部實現是用數組+鏈表,第一維的數組出現碰撞,則存到鏈表里面去。Rehash 的時候,為了不阻塞服務,采用的是漸進式的 Rehash,保留2個 Hash,逐漸在指令執行或者定時任務中,將數據從老的 Hash 遷移到新的 Hash
使用場景:購物車、頻繁變化的屬性
列表
Redis 的列表按照插入順序排序,相當于 Java 語言里面的 LinkedList,注意它是鏈表而不是數組。這意味著 list 的插入和刪除操作非常快,時間復雜度為 O(1),但是索引定位很慢,時間復雜度為O(n)。當列表彈出了最后一個元素之后,該數據結構自動被刪除,內存被回收
Redis 的列表結構常用來做異步隊列使用,將需要延后處理的任務結構體序列化成字符串塞進 Redis 的列表,另一個線程從這個列表中輪詢數據進行處理
列表不同進出棧的方式,產生的不同數據結構
LPUSH+LPOP = 棧
LPUSH+RPOP = 隊列
LPUSH+ LTRIM = 固定數量的列表
LPUSH +BRPOP = 消息隊列
使用場景:異步隊列
無序集合
set 相當于 Java 的 HashSet,無序的鍵值對,不過其值都是NULL,鍵不可以重復。數據量較少且是整數的時候用有序數組,較大的時候采用散列表
應用場景:標簽、社交、隨機數
有序集合
zset 可能是 Redis 提供的最為特色的數據結構,它類似于 Java 的 SortedSet 和 HashMap 的結合體,一方面它是一個 set,保證了內部 value 的唯一性,另一方面它可以給每個 value 賦予一個 score,代表這個 value 的排序權重。
它的內部實現用的是一種叫「跳躍列表」的數據結構,之所以「跳躍」,是因為內部的元素可能「身兼數職」
一個元素,同時處于 L0、L1 和 L2 層,可以快速在不同層次之間進行「跳躍」,定位插入點時,先在頂層進行定位,然后下潛到下一級定位,一直下潛到最底層找到合適的位置,將新元素插進去
應用場景:value 為粉絲 ID,score 是關注時間,可以按照關注時間順序給出粉絲 ID;value 是學生 ID,score 是其分數,可以按照分數排序
Redis 持久化
Redis 提供了兩種持久化的方式,分別是RDB(Redis DataBase)和AOF(Append Only File)
RDB,簡而言之,就是在不同的時間點,將 Redis 存儲的數據生成快照并存儲到磁盤等介質上
AOF,則是換了一個角度來實現持久化,那就是將 Redis 執行過的所有寫指令記錄下來,在下次 Redis 重新啟動時,只要把這些寫指令從前到后再重復執行一遍,就可以實現數據恢復了
其實 RDB 和 AOF 兩種方式也可以同時使用,在這種情況下,如果 Redis 重啟的話,則會優先采用 AOF 方式來進行數據恢復,這是因為 AOF 方式的數據恢復完整度更高
AOF 有一個配置屬性 sync,就是用來同步命令到磁盤的,如果我們對于 Redis 的性能要求不高,則可以在每條寫指令時都 sync 一下磁盤,這樣即使在突然斷電的情況下,也能保證數據的最小丟失率
RDB
RDB 是將 Redis 某一時刻的數據持久化到磁盤中,是一種快照式的持久化方法
Redis 在進行數據持久化的過程中,會先將數據寫入到一個臨時文件中,待持久化過程都結束了,才會用這個臨時文件替換上次持久化好的文件。正是這種特性,讓我們可以隨時來進行備份,因為快照文件總是完整可用的
對于 RDB 方式,Redis 會單獨創建(fork)一個子進程來進行持久化,而主進程是不會進行任何 IO 操作的,這樣就確保了 Redis 極高的性能,子進程創建后,父子進程共享數據段,父進程繼續提供讀寫服務,寫臟的頁面數據會逐漸和子進程分離開來
如果需要進行大規模數據的恢復,且對于數據恢復的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加的高效
AOF
AOF,英文是 Append Only File,即只允許追加不允許改寫的文件
如前面介紹的,AOF 方式是將執行過的寫指令記錄下來,在數據恢復時按照從前到后的順序再將指令都執行一遍,就這么簡單
默認的 AOF 持久化策略是每秒鐘 fsync 一次(fsync是指把緩存中的寫指令記錄到磁盤中),因為在這種情況下,Redis 仍然可以保持很好的處理性能,即使 Redis 故障,也只會丟失最近1秒鐘的數據
RDB 與 AOF 比較
Redis 主從
Redis 是支持主從同步的,而且也支持一主多從以及多級從結構
主從結構,一是為了純粹的冗余備份,二是為了提升讀性能,比如很消耗性能的 sort 就可以由從服務器來承擔
Redis 的主從同步是異步進行的,這意味著主從同步不會影響主邏輯,也不會降低 Redis 的處理性能
主從架構中,可以考慮關閉主服務器的數據持久化功能,只讓從服務器進行持久化,這樣可以提高主服務器的處理性能
在主從架構中,從服務器通常被設置為只讀模式,這樣可以避免從服務器的數據被誤修改。但是從服務器仍然可以接受 CONFIG 等指令,所以還是不應該將從服務器直接暴露到不安全的網絡環境中
舊版復制功能
Redis 的復制功能分為同步(sync)和命令傳播(command propagate)兩個操作:
同步操作用于將從服務器的數據庫狀態更新至主服務器當前所處的數據庫狀態
命令傳播操作則用于在主服務器的數據庫狀態被修改,導致主從服務器的數據庫狀態出現不一致時,讓主從服務器的數據庫重新回到一致狀態
同步過程
當客戶端向從服務器發送 SLAVEOF 命令,要求從服務器復制主服務器時,從服務器首先需要執行同步操作,也就是將從服務器的數據庫狀態更新至主服務器當前所處的數據庫狀態
從服務器對主服務器的同步操作需要通過向主服務器發送 SYNC 命令來完成,以下是 SYNC 命令的執行步驟:
1)從服務器向主服務器發送 SYNC 命令
2)收到 SYNC 命令的主服務器執行 BGSAVE 命令,在后臺生成一個 RDB 文件,并使用一個緩沖區記錄從現在開始執行的所有寫命令
3)當主服務器的 BGSAVE 命令執行完畢時,主服務器會將 BGSAVE 命令生成的 RDB 文件發送給從服務器,從服務器接收并載入這個 RDB 文件,將自己的數據庫狀態更新至主服務器執行 BGSAVE 命令時的數據庫狀態
4)主服務器將記錄在緩沖區里面的所有寫命令發送給從服務器,從服務器執行這些寫命令,將自己的數據庫狀態更新至主服務器數據庫當前所處的狀態
缺點
這種復制功能,雖然可以很好的完成主備之間的數據同步,但是效率確實非常低的
每次執行 SYNC 命令,主從服務器需要執行以下動作:
1)主服務器需要執行 BGSAVE 命令來生成 RDB 文件,這個生成操作會耗費主服務器大量的 CPU、內存和磁盤 I/O 資源
2)主服務器需要將自己生成的 RDB 文件發送給從服務器,這個發送操作會耗費主從服務器大量的網絡資源(帶寬和流量),并對主服務器響應命令請求的時間產生影響
3)接收到 RDB 文件的從服務器需要載入主服務器發來的 RDB文件,并且在載入期間,從服務器會因為阻塞而沒辦法處理命令請求
4)BGSAVE 命令產生的 RDB 文件是主服務器鎖包含的所有數據,這是一個全量過程
因為 SYNC 命令是一個如此耗費資源的操作,所以 Redis 有必要 保證在真正有需要時才執行 SYNC 命令
新版復制功能
為了解決舊版本復制功能的低效問題,Redis 從2.8版本開始,使用 PSYNC 命令代替 SYNC 命令來執行復制時的同步操作
PSYNC 命令具有完整重同步(full resynchronization)和部分重同步(partial resynchronization)兩種模式:
完整重同步用于處理初次復制情況:完整重同步的執行步驟SYN命令的執行步驟基本一樣,它們都是通過讓主服務器創建并發RD文件,以及向從服務器發送保存在緩沖區里面的寫命令來進行同步
部分重同步則用于處理斷線后重復制情況:當從服務器在斷線后重新連接主服務器時,如果條件允許,主服務器可以將主從服務器連接斷開期間執行的寫命令發送給從服務器,從服務器只要接收并執行這些寫命令,就可以將數據庫更新至主服務器當前所處的狀態
PSYNC 命令的部分重同步模式解決了舊版復制功能在處理斷線后 重復制時出現的低效情況,即采用增量同步的形式,大大節省了服務器資源
哨兵與集群
Redis Sentinal 主要用于高可用,在 master 宕機時會自動將 slave 提升為 master,繼續提供服務
Redis Cluster 則側重于擴展性,在單個 Redis 內存不足時,使用 Cluster 進行分片存儲
Redis Sentinal
Sentinel(哨崗、哨兵)是 Redis 的高可用性(high availability)解決方案:由一個或多個 Sentinel 實例組成的 Sentinel 系統可以監視任意多個主服務器,以及這些主服務器屬下的所有從服務器,并在被監視的主服務器進入下線狀態時,自動將下線主服務器屬下的某個從服務器升級為新的主服務器,然后由新的主服務器代替已下線的主服務器繼續處理命令請求
初始狀態下,Server1 為主服務器,其余為從服務器
假設這時,主服務器 Server1 進入下線狀態,那么從服務器 Server2、Server3、Server4 對主服務器的復制操作將被中止,并且 Sentinel 系統會察覺到 Server1 已下線
當 Server1 的下線時長超過用戶設定的下線時長上限時,Sentinel 系統就會對 Server1 執行故障轉移操作:
首先,Sentinel 系統會挑選 Server1 屬下的其中一個從服務器,并將這個被選中的從服務器升級為新的主服務器
之后,Sentinel 系統會向 Server1 屬下的所有從服務器發送新的復制指令,讓它們成為新的主服務器的從服務器,當所有從服務器都開始復制新的主服務器時,故障轉移操作執行完畢
另外,Sentinel 還會繼續監視已下線的 Server1,并在它重新上線時,將它設置為新的主服務器的從服務器
選取新主
老主恢復,降為從服務器
Redis Cluster
Redis 集群是 Redis 提供的分布式數據庫方案,集群通過分片(sharding)來進行數據共享,并提供復制和故障轉移功能
Redis Cluser 采用虛擬槽分區,所有的鍵根據哈希函數映射到0~16383整數槽內,計算公式:slot=CRC16(key)&16383
每一個節點負責維護一部分槽以及槽所映射的鍵值數據
Redis 虛擬槽分區解耦了數據與節點之間的關系,簡化了節點擴容和收縮的難度
當然 Redis 集群也有很多功能上的限制
1)key 批量操作支持有限。如 mset、mget,目前只支持具有相同 slot 值的 key 執行批量操作。對于映射為不同 slot 值的 key 由于執行 mget、mget 等操作可能存在于多個節點上因此不被支持
2)key 事務操作支持有限。同理只支持多 key 在同一節點上的事務操作,當多個 key 分布在不同的節點上時無法使用事務功能
3)key 作為數據分區的最小粒度,因此不能將一個大的鍵值對象如 hash、list 等映射到不同的節點
4)不支持多數據庫空間。單機下的 Redis 可以支持16個數據庫,集群模式下只能使用一個數據庫空間,即db0
5)復制結構只支持一層,從節點只能復制主節點,不支持嵌套樹狀復制結構
更新策略
緩存中的數據通常都是有生命周期的,需要在指定時間后被刪除或更 新,這樣可以保證緩存空間在一個可控的范圍。但是緩存中的數據會和數據源中的真實數據有一段時間窗口的不一致,需要利用某些策略進行更新。
下面介紹 Redis 常用的三種緩存更新策略
LRU/LFU/FIFO 算法剔除
剔除算法通常用于緩存使用量超過了預設的最大值的時候,如何對現有的數據進行剔除,例如使用 maxmemory-policy 這個配置作為內存最大值后對于數據的剔除策略
超時刪除
超時剔除通過給緩存數據設置過期時間,讓其在過期時間后自動刪除,例如 Redis 提供的 expire 命令。如果業務可以容忍一段時間內,緩存層數據和存儲層數據不一致,那么可以為其設置過期時間。在數據過期后,再從真實數據源獲取數據,重新放到緩存并設置過期時間
主動更新
應用方對于數據的一致性要求高,需要在真實數據更新后,立即更新緩存數據。例如可以利用消息系統或者其他方式通知緩存更新
更新策略對比
從上面的橫向對比數據,我們可以得出如下建議配置
低一致性業務建議配置最大內存和淘汰策略的方式
高一致性業務可以結合使用超時剔除和主動更新,這樣即使主動更新出了問題,也能保證數據過期時間后刪除臟數據
Redis 緩存雪崩、擊穿和穿透
這是三個 Redis 最為常見的三個問題,我們逐一來了解下
1.雪崩
什么是雪崩
由于緩存層承載著大量請求,有效地保護了存儲層,但是如果緩存層由于某些原因不能提供服務,于是所有的請求都會達到存儲層,存儲層的調用量會暴增,造成存儲層也會級聯宕機的情況
預防和解決辦法
保證緩存層服務高可用性
出現服務不可用的情況,我們第一時間想到的肯定是高可用,甚至是異地容災等機制
依賴隔離組件為后端限流并降級
無論是緩存層還是存儲層都會有出錯的概率,可以將它們視同為資源。作為并發量較大的系統,假如有一個資源不可用,可能會造成線程全部阻塞(hang)在這個資源上,造成整個系統不可用。降級機制在高并發系統中是非常普遍的:比如推薦服務中,如果個性化推薦服務不可用,可以降級補充熱點數據,不至于造成前端頁面開天窗
數據預熱
針對大量緩存同時過期的情況,可以通過緩存 reload 機制,預選去更新緩存,在即將發生大并發訪問前手動觸發加載緩存不同的 key,設置不同的過期時間,讓緩存失效的時間點盡量均勻
2.擊穿
如果緩存中的某個熱點數據過期了,此時大量的請求訪問了該熱點數據,就無法從緩存中讀取,直接訪問數據庫,數據庫很容易就被高并發的請求沖垮,這就是緩存擊穿的問題
擊穿其實可以看做是雪崩的一個子集,解決方法一般有兩種,設置熱點數據永不過期和設置互斥鎖
所謂的互斥鎖,就是保證同一時間只有一個業務線程更新緩存,對于沒有獲取互斥鎖的請求,要么等待鎖釋放后重新讀取緩存,要么就返回空值或者默認值
3.穿透
緩存穿透是指查詢一個根本不存在的數據,緩存層和存儲層都不會命 中,導致請求在訪問緩存時,發現緩存缺失,再去訪問數據庫時,發現數據庫中也沒有要訪問的數據,沒辦法構建緩存數據,來服務后續的請求。那么當有大量這樣的請求到來時,數據庫的壓力驟增,產生穿透問題
緩存穿透問題可能會使后端存儲負載加大,由于很多后端存儲不具備高 并發性,甚至可能造成后端存儲宕掉
解決方案
緩存空對象
當存儲層不命中后,仍然將空對象保留到緩存層中,之后再訪問這個數據將會從緩存中獲取,這樣就保護了后端數據源
當然緩存空對象會有兩個問題:
第一,空值做了緩存,意味著緩存層中存了更多的鍵,需要更多的內存空間(如果是攻擊,問題更嚴重),比較有效的方法是針對這類數據設置一個較短的過期時間,讓其自動剔除
第二,緩存層和存儲層的數據會有一段時間窗口的不一致,可能會對業務有一定影響。例如過期時間設置為5分鐘,如果此時存儲層添加了這個數據,那此段時間就會出現緩存層和存儲層數據的不一致,此時可以利用消息系統或者其他方式清除掉緩存層中的空對象
布隆過濾器攔截
在訪問緩存層和存儲層之前,將存在的 key 用布隆過濾器提前保存起來,做第一層攔截,即使發生了緩存穿透,大量請求只會查詢 Redis 和布隆過濾器,而不會查詢數據庫,保證了數據庫能正常運行
這種方法適用于數據命中不高、數據相對固定、實時性低(通常是數據 集較大)的應用場景,代碼維護較為復雜,但是緩存空間占用少
緩存空對象與布隆過濾器比較
好了,redis的功能還是非常強大的,尤其是在一些秒殺的系統或者是需要高速訪問的地方,用mysql會比較慢,我們上次寫了一個簡單的小應用,里面就用到redis.(女友想買個手機!我用Python做了個比價機器人了!)
這就是今天的內容,如果你覺得對你有所幫助,就點贊+在看支持一下吧~
也歡迎大家關注我們的視頻號,里面有很多好玩的視頻。
推薦閱讀: 入門:?最全的零基礎學Python的問題? |?零基礎學了8個月的Python??|?實戰項目?|學Python就是這條捷徑 干貨:爬取豆瓣短評,電影《后來的我們》?|?38年NBA最佳球員分析?|? ?從萬眾期待到口碑撲街!唐探3令人失望? |?笑看新倚天屠龍記?|?燈謎答題王?|用Python做個海量小姐姐素描圖?|碟中諜這么火,我用機器學習做個迷你推薦系統電影 趣味:彈球游戲? |?九宮格? |?漂亮的花?|?兩百行Python《天天酷跑》游戲! AI:?會做詩的機器人?|?給圖片上色?|?預測收入?|?碟中諜這么火,我用機器學習做個迷你推薦系統電影 小工具:?Pdf轉Word,輕松搞定表格和水印!?|?一鍵把html網頁保存為pdf!|??再見PDF提取收費!?|?用90行代碼打造最強PDF轉換器,word、PPT、excel、markdown、html一鍵轉換?|?制作一款釘釘低價機票提示器!?|60行代碼做了一個語音壁紙切換器天天看小姐姐!|年度爆款文案
1).臥槽!Pdf轉Word用Python輕松搞定!
2).學Python真香!我用100行代碼做了個網站,幫人PS旅行圖片,賺個雞腿吃
3).首播過億,火爆全網,我分析了《乘風破浪的姐姐》,發現了這些秘密?
4).80行代碼!用Python做一個哆來A夢分身?
5).你必須掌握的20個python代碼,短小精悍,用處無窮?
6).30個Python奇淫技巧集?
7).我總結的80頁《菜鳥學Python精選干貨.pdf》,都是干貨?
8).再見Python!我要學Go了!2500字深度分析!
9).發現一個舔狗福利!這個Python爬蟲神器太爽了,自動下載妹子圖片
點閱讀原文,看200個Python案例!
總結
以上是生活随笔為你收集整理的太牛了,值得收藏!7000字22张图,精讲 Redis 知识!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用Python执行SQL、Excel常见
- 下一篇: 全国计算机等级考试三级数据库知识点总结