Redis學習筆記

1、基礎知識

(一)Redis的線程模型

其中:node

1.多路IO複用器。

負責輪詢監聽服務器套接字和客戶端套接字,這裏是NIO的機制,因此每一個套接字中間的可讀,可寫狀態沒有阻塞,避免了線程花費時間等待可讀、可寫狀態。多路IO複用器監聽以後生成了各個事件,分爲可讀和可寫兩類並攜帶對應套接字,而後遍歷事件將其push到一個隊列中去,由文件事件分派器去異步處理。redis

2. 文件事件分派器。

它是單線程的,處理一個set命令的業務操做是原子的。文件事件分派器負責根據事件分派到不一樣的事件應答器去處理。這裏分派的邏輯是:算法

  • 若是判斷是服務器套接字產生的可讀事件則交由應答處理器處理,應答處理器會建立一個客戶端套接字,建立這個套接字表明客戶端就能夠向服務器發送命令了,當下次這個客戶端套接字被監聽到可讀事件時表明服務器接收到了命令。
  • 若是判斷是客戶端套接字產生的可讀事件則交由命令請求處理器處理,處理器執行redis真正的業務操做,修改內存數據。以後命令請求處理器會將客戶端套接字與命令回覆處理器關聯,以便下次監聽到客戶端嘗試讀取的命令的時候進行回覆。
  • 若是判斷是客戶端套接字產生的可寫事件則交由命令回覆處理器處理,處理器向套接字寫入成功回覆,而後接觸套接字與命令回覆器的關聯。

(二)Redis的數據類型

包括string,list(提及是List,其實就是JAVA中的有序隊列),hash散列表,set(無序,元素不重複),sortedset(有序,元素不重複,set時須要指定分數,排序根據分數來排)。數據庫

(三)Redis過時刪除策略

過時刪除有三種狀況:緩存

1.按期刪除

默認每隔100ms隨機抽取一些設置了過時時間的key刪除。服務器

2.惰性刪除

後面在獲取某個key時,redis會檢查key是否設置了過時時間是否過時,若是過時就刪除,不返回任何東西。網絡

3.內存淘汰機制時刪除

指的是,內存不夠了,redis會運行淘汰部分鍵,這裏根據參數不一樣採起不一樣的策略,其中有:架構

  • noeviction,新寫入會報錯
  • allkeys-lru,把最近最少使用的key刪除
  • allkeys-random,隨機移除key
  • volatile-lru,在設置了過時時間的鍵中,移除最近最少使用的key
  • volatile=random,在設置了過時時間的鍵中,隨機刪除
  • volatile-ttl,在設置了過時時間的key中,刪除立刻要過時的key

(四)持久化

Redis持久化有兩種:RDB機制和AOF機制。其中RDB機制是定時全量在磁盤中備分內存中的數據。AOF機制是以命令日誌的方式在磁盤中備份數據。當master節點宕機重啓後,會根據持久化的數據還原到內存中。若是RDB和AOF被同時開啓,將優先用AOF來進行數據恢復。併發

兩種機制的對比:框架

  • QPS影響,AOF對QPS的影響是持續性的,RDB則只是間隔性的。
  • 恢復速率,因爲RDB不用重放命令,因此恢復速率RDB較快。
  • 數據丟失量,因爲AOF是近乎實時的,因此比RDB丟失的要少的多。
  • 冷備份簡易度,RDB很是適合作冷備份,通常的作法是用腳本將RDB文件自動同步到雲上,這樣可使得Master節點磁盤文件打不開或者丟失,或者機器都重啓不了也能使用冷備份文件複製到其餘節點進行故障恢復,

2、redis的架構模式

redis架構模式有兩種,一種是主從架構,一種是rediscluster架構。下面先總結下主從架構。

(一)主從架構

主從架構相比rediscluster的優勢是能夠讀寫分離,但缺點是不能夠經過加服務器的方式橫向擴展redis內存空間。

1.節點結構

主從架構由1個master節點,n個slave節點構成,master節點負責寫,slave節點負責讀。

2.節點數據同步策略

(1)full resynchronization(全量複製)

全量複製場景通常是在slave第一次啓動或者長時間離線致使數據和master節點相差過大的時候產生的。其過程是master節點後臺開啓線程將內存中最新的數據全量生成一份rdb文件,同時這段時間內寫入命令也緩存在內存中。slave收到rdb文件以後先持久化到本身磁盤,而後寫入本身的緩存,而後master把前面內存中緩存的寫命令發送給slave節點,slave再執行一遍寫命令。這個過程叫作全量複製。

注:

  • 若一段時間內master收到多個slave節點的sync命令(相對PSYNC增量同步,SYNC表明請求全量複製),那麼master只會生成一份rdb快照文件,不會重複生成。
  • 2.8開始後支持斷點續傳,master和slave都會保存一個replica offset,用於記錄斷點的位置,因此若是短期slave重連不會請求全量複製,默認發送PSYNC命令,請求增量複製。
  • 支持無磁盤化複製,開啓master將在內存中生成rdb文件。涉及的參數是repl-diskless-sync no(默認no,不使用diskless同步方式 ),repl-diskless-sync-delay 5(默認5秒,等待時間內多個slave鏈接的話都傳這一個rdb)。

(2)異步複製

異步複製指的是後續節點保持同步是經過異步複製完成的,master節點收到寫命令,寫完成後返回客戶端寫成功,而後異步的複製給slave節點。

3.哨兵架構

要保證主從架構的高可用必須配置哨兵集羣,故障轉移是經過哨兵架構來完成的,哨兵是不一樣的進程,也是一個集羣。下面主要介紹下哨兵架構。

(1)哨兵集羣通訊

其內部通訊機制基於Redis的發佈訂閱,一個哨兵集羣會訂閱redis同一個channel。經過同一個channel交換信息。

(2)sdown和odown

sdown-主觀宕機,描述的master節點狀態被部分哨兵節點認爲宕機,可是沒有真正觸發到主從切換的條件。

odown-客觀宕機,描述的是master節點狀態觸發了主從切換的條件。

注:當哨兵節點Ping master節點若是延遲超過了is-master-down-agter-millseconds指定毫秒數以後,該節點就認爲Master節點宕機

(2)主從切換的條件

配置文件的quorum參數,quorum配置的是執行主從切換須要多少個哨兵節點認爲master節點宕機。

(3)主從切換的過程

當判斷master客觀宕機後,若是哨兵在線節點超過majority數量(哨兵集羣的半數或半數以上),則會選舉一個哨兵節點出來執行主從切換,這是這個哨兵節點又會根據slave節點跟master斷開鏈接的時長、slave配置的優先級、複製offset、run id(slave節點的惟一標識)來進行篩選和排序,選舉一個最優的slave節點出來繼任master。其中斷開鏈接時長若是超過down-after-millseconds(容許master正常延遲時間極限)的10倍加上millseconds_since_master_is_in_sdown_state(master宕機時長)的話,就會排除選舉範圍以外,剩下的依次根據slave priority(slave配置的優先級)、再根據slae offset排序(越靠後越高)、再根據run id排序(越小優先級越高)來進行排序。爲了保證短期內屢次切換,其餘哨兵節點不知道master節點時哪一個節點,每次執行主從切換時,哨兵會從要切換的從節點獲取一個版本號,成功後將版本號發佈到全部哨兵節點監聽的channel上,其餘哨兵依次更新master節點的信息。

(4)哨兵集羣的高可用性

因爲哨兵和redis機器是分不開的,哨兵集羣做爲redis高可用保證,自身也應該保證是高可用的。因此通常至少須要3個哨兵節點,由於若是隻有2個哨兵節點,掛的master節點的機器上又有哨兵進程,那麼這時哨兵節點就只剩下兩個,而2的majority數又是2,這時是沒法完成主備切換的。

4.主從+哨兵的數據丟失問題

(1)場景

  • 異步複製致使的數據丟失,master在正在準備異步向slave節點同步數據的狀況下宕機,slave節點被選舉爲master,會丟掉沒有完成異步複製的數據。
  • 集羣腦裂致使的數據丟失,因爲網絡分區故障,master與哨兵斷了聯繫,但和客戶端又保持着聯繫,致使新的Master節點被選舉,當網絡分區故障恢復後,新的master往原來的Master節點同步數據,致使在故障期間原來的master節點收到的數據被丟失。

(2)解決方案

  • 一是須要注意兩個參數,一個是min-slaves-to-write(默認1),表示slave節點最少要保證有多少個存活,min-slaves-max-lag(默認10),表示全部slave節點延遲不能同時超過10秒。當這兩個參數不知足的時候,master節點就會拒絕客戶端的寫請求,以此控制丟失的數據量不會過高。
  • 二是客戶端要進行對應的降級處理,如寫到本地數據庫或發送到消息隊列中,而後每隔一段時間重試鏈接master,當master節點恢復後,在限流的往master寫數據,避免流量涌入過多超過master節點寫負載。

(二)redis cluster集羣架構

redis cluster集羣架構與主從架構+哨兵模式的區別是,redis cluster集羣架構針對的是大數據量場景,其能夠橫向擴展多個master節點,可是redis官方默認不支持redis cluster的讀寫分離,其slave只用於故障轉移。但能夠經過readonly命令,將slave設置成可讀,而後經過slave獲取相關的key,達到讀寫分離。若是在redis cluster中強行採用讀寫分離可能致使讀到過時的數據,在Java的Redis操做類庫中,如Jedis和Lettuce都默認只針對Master進行讀寫,若須要修改Jedis則只能作源碼級別的改動,Letture修改相比較簡單一些,開放了setReadFrom方法,能夠經過connection.setReadFrom(ReadFrom.SLAVE)指定讀往slave節點讀。

1.節點結構

多master節點,多slave。

2.數據分佈算法

數據分佈算法通常有三種經常使用算法:

  • 普通的hash取模算法。即經過key的hash值再與master節點數量取模,模爲多少就寫入或讀取哪一個master節點。但這種方法有個缺點就是,若是有一個節點失效,master節點數量被改變,咱們若是要取某個數據,計算的模打到的節點就與以前的節點不同了,這就致使之前寫入的節點基本上所有失效,必須經過大量的計算從新取模在把數據從新存入剩下的節點。
  • 一致性hash算法。一致性hash算法主要是將hash值的整個空間組成一個圓環,每一個節點均勻排列在一個圓環上,一個key對應的節點時經過計算Key值的hash值而後判斷在圓環的哪一個位置,順時針找到的第一個節點就是咱們寫入或讀取的節點。這種算法能夠解決若是一個節點失效,則失效的數據只是該節點的數據,但該失效節點的負載數據請求至關於所有打在了順時針下一個節點上,優化策略是在圓環上均勻分佈大量的虛擬節點替代本來的真實節點,每一個真實節點用相同數量的虛擬節點用來表示。因爲虛擬節點順序也是隨機的,因此若是失效了某個節點,那麼這個節點本來所承載的hash值空間也會均勻分佈到剩下的節點當中,這個優化可使得失效節點的數據請求均勻分佈到剩下的節點中。
  • hashSlot算法。redis採用的就是hashSlot算法,每一個節點均勻接管0-16384的模(稱爲hashslot),而後key來了以後,先判斷與16384的模是多少,獲取到模(hashSlot)以後,判斷hashSlot是被哪一個節點接管,再尋找該節點寫入或讀取,這能保證當某個節點宕機以後,失效的只是該節點接管的hashslot對應的數據。redis會自動將該失效節點接管的hashslot均勻分配到剩下的節點,因此這也能保證該失效節點本來負載的請求量均勻分配到剩下節點。

3.節點間通訊機制

在集羣內部存在的元數據包括hashslot和Node的映射表、master和slave關係、、故障信息、節點的增長和移除等等是如何通訊的勒?redis使用的是Gossip協議來通訊維護這些元數據,每一個節點將開放兩個端口,一個端口是面向客戶端提供服務的端口,另外一個端口端口號是服務端口+10000集羣內部節點通訊的端口,稱爲cluter bus集羣總線。Gossip將消息分爲4種類型:

  • meet,某個節點發送一個goosip meet消息,通知哪一個節點加入集羣
  • ping,每一個節點都會頻繁的給其餘節點發送Ping,其中包含本身的狀態還有本身維護的集羣元數據,互相經過Ping交換元數據進行更新。
  • pong, 返回Ping和meet,包含本身的狀態和其餘信息,也可用於信息廣播和更新
  • fail, 某個節點判斷另外一個節點fail以後,就發送fail給其餘節點,通知其餘節點,指定的節點宕機了

4.redisCluster的主備切換

redisCluster的主備切換相似哨兵模式,其主要流程是:

  • 判斷節點宕機。對應哨兵模式的客觀宕機,SDown在RedisCluster稱爲PFail,主觀宕機ODown稱爲Fail。在配置的cluster-node-timeout時間內,某個節點一直沒有返回Pone,將會被髮送ping消息的節點認爲是Pfail。若是一個節點認爲某個節點Pfail了,那麼會在集羣總線中,ping其餘節點,若是超過半數的節點都認爲pfail了,那麼就會變成fail,此時將進行主備切換。
  • 從節點過濾。若slave節點與master節點斷開鏈接時間超過了cluster-node-timeout 乘以 cluster-slave-validity-factor,就會被排除選舉資格。
  • 從節點選舉。每一個master,根據slave的offset指定slave的選舉時間,選舉時間越靠前,offset最大的slave節點優先開始投票讓其餘master節點選舉,而後得到大部分投票的從節點將成爲新的Master節點(投票策略應該也是判斷offset)。

5.Jedis類庫與RedisCluster交互的模式

使用原生命令與Redis集羣交互主要一種是隨意鏈接一個節點,在寫入數據時,由該節點計算key的hashSlot值,若是該節點不是對應key的處理節點,該請求將重定向到對應節點。另外,在命令上能夠加入hashtag手動指定hashslot,如:set myke1:{hash tag} 值,這樣免除了節點實例計算hashSlot.

在Jedis框架裏本地維護一個hashslot映射表緩存,工做流程失在rediscluster初始化的時候隨機找一個節點初始化hashslot映射表到本地,後面發送請求的時候根據映射表尋找節點,若是節點返回Moved(表示進行了重定向),就以該節點元數據再更新一下本身的緩存。另外,若是hashslot正在遷移,將返回ask(表示hashslot正在遷移中),jedicluster會重定向到目標節點嘗試請求,但Jedisscluster不會進行更新映射表。

3、緩存方案-Cache Aside Pattern

指的是:

讀的時候,先讀緩存,緩存沒有的話,讀數據庫,取出數據後放入緩存,同時返回響應。

更新的時候,先更新數據庫,在刪除緩存

這裏主要涉及兩個問題,集中在更新的時候:

  • 第一是爲何是刪除緩存而不是更新,由於若是更新緩存會破壞緩存意義,更新並不表明這個數據是熱點訪問數據,若是更新可能會破壞緩存的LRU特性,更新緩存應該以訪問的動做爲驅動。另外有時候數據庫數據關聯的緩存是通過必定計算才關聯的,若是須要去更新緩存可能涉及的計算比較複雜。

  • 第二是爲何是先更新數據庫,在刪除緩存,由於這是爲了不緩存與數據庫雙寫不一致,如:若修改數據數據庫事務還沒提交,可是已經把緩存從redis中刪除,此時來了個讀請求,會把舊的數據刷到緩存裏面,這樣就致使了緩存中的數據直到下一次修改數據庫以前確定是與數據庫不一致的。

4、redis緩存常見問題整理

(一)緩存與數據庫雙寫不一致

1.場景

讀寫併發致使,讀的時候沒讀到緩存,同時寫的時候刪除了緩存但還沒來得及更新數據庫,致使讀的時候去更新緩存更新成了舊的數據。

2.應對方案

先更新數據庫,在刪除緩存,同時用redis主從提升redis可用性,避免redis刪除操做失敗,另外若是是讀寫分離的狀況能夠用阿里的canel組件,監遵從庫的binlog,再去消費消息刪除緩存。

(二)緩存雪崩

1.場景

因爲緩存宕機了,大量請求沒有命中緩存,數據庫接收大量請求壓力

2.應對方案

  • 事前,緩存必須高可用,加哨兵或者cluseter下配置slave
  • 事中,本地添加ehcahe(spingboot集成)緩存做爲二級緩存,若是集成了hytrix組件則在前面兩級緩存沒命中以後加上限流+降級策略。
  • 過後,開啓redis的AOF和RDB,儘快恢復redis可用。

(三)緩存穿透

1.場景

大量惡意攻擊(請求數據庫沒有的數據)請求,直接擊穿了緩存,每次都去走數據庫。

2.應對方案

一是要作好事前的參數校驗,高級一點就用布隆過濾器(至關於全部數據都用必定算法放在一個字典中,經過數據hash只判斷在字典中存不存在)。 二是採起暴力一點的辦法沒查到就寫一個空值到緩存裏去(注意設置過時時間),這種有必定限制效果,由於惡意攻擊方必須耗費精力準備足夠大的參數字典來攻擊。

(四)緩存併發競爭

1.場景

短期內針對一個key的更新併發,致使緩存更新沒有保證順序,因爲順序錯亂,可能緩存的數據就錯了。

2.應對方案

使用分佈式鎖,能夠基於zookeeper或redis本身,串行化某個key的更新操做,拿到鎖以後,判斷這把鎖的時間戳(value)是否比本身的要新,新的話就不更新緩存了。反之將本身的時間戳打上這把鎖,並更新緩存。

相關文章
相關標籤/搜索