【JAVA程序員進階之路】Redis基礎知識兩篇就知足(二)

導言

你們好,我是南橘,從接觸java到如今也有差很少兩年時間了,兩年時間,從一名連java有幾種數據結構都不懂超級小白,到如今懂了一點點的進階小白,學到了很多的東西。知識越分享越值錢,我這段時間總結(包括從別的大佬那邊學習,引用)了一些日常學習和麪試中的重點(自我認爲),但願給你們帶來一些幫助java

這篇文章的出現,首先要感謝一我的三太子敖丙 ,就是他的文章讓我發現,原來Redis的知識如此的多姿多彩。恩恩,他的文章,我是期期都看node

這是這篇文章的思惟導圖,由於用的是免費版的軟件,因此有很多水印,須要原版的能夠問我要程序員

Redis篇,由於時間和篇幅的緣由,並無一次性寫完,因而乎,分紅了上下兩篇,沒有看過上半部分的小夥伴能夠去看一下~面試

Redis基礎知識兩篇就知足(一)redis

1、緩存雪崩、擊穿、穿透

這一次,從Redis最爲人津津樂道(面試也常常常問)的緩存三崩壞來講起算法

緩存雪崩

顧名思義,你們應該都見過雪崩,南橘我更是遠遠地親眼見過,那場景,很有種天崩地裂的感受,而對於數據庫來講,緩存雪崩,也說得上是一種天崩地裂了。 同一時間Redis緩存大面積失效,那一瞬間Redis跟不存在同樣,這個時候數據直接請求到數據庫。你想一想,緩存的意義就是減小DB,若是緩存沒有了,大量的請求還不直接打爆數據庫?數據庫

緩存雪崩如何出現的?數組

  • 大量的緩存同時失效,多是同時間生成,同時間到期
  • 緩存同時被刪除
  • 緩存層出現了錯誤,不能正常工做了

解決辦法:緩存

  • 一、批量網Redis存數據的時候,把每一個KEY失效時間都增長隨機值,保證不會同時失效
  • 二、設置熱點數據永不過時,有更新操做就更新緩存(可是這個方法很差,永不過時致使緩存大量堆積,不少緩存不必定有用)
  • 三、Redis集羣部署,將熱點數據均勻分佈在不一樣Redis庫中也能避免所有失效,避免了Redis出現問題致使緩存雪崩

緩存擊穿

有一個Key很是熱點,在不停扛着大併發,大併發集中對這一點進行訪問,當這個Key失效的瞬間、大量併發擊穿緩存,直接訪問數據庫。bash

其實緩存擊穿,真的算不上什麼特別大的問題,畢竟不是每一個公司都在同一個Key上都有那麼大的熱點,只須要設置好過時時間,穩定好Redis集羣,緩存擊穿不難避免。

解決辦法:

  • 一、設置熱點數據永不過時
  • 二、增長互斥鎖【簡單地來講,就是在緩存失效的時候(判斷拿出來的值爲空),不是當即去load db,而是先使用緩存工具的某些帶成功操做返回值的操做(好比Redis的SETNX或者Memcache的ADD)去set一個mutex key,當操做返回成功時,再進行load db的操做並回設緩存】

在個人經驗來看,設置互斥鎖顯然沒有必要,一個熱點永不過時就能解決的問題,爲何還要用到鎖?這不是平白增長複雜度嗎?也許在特殊場景能看到,可是對於我這個小白來講,僅僅能在各位大牛的博客裏看到這個觀點。

緩存穿透

從名字上來看,緩存擊穿和緩存穿透很像,實際上頁比較像,可是既然區分了出來,天然有一些不一樣的地方

緩存穿透的概念很簡單,用戶想要查詢一個數據,發現redis內存數據庫沒有,也就是緩存沒有命中,因而向持久層數據庫查詢。發現也沒有,因而本次查詢失敗。當用戶不少的時候,緩存都沒有命中,因而都去請求了持久層數據庫。這會給持久層數據庫形成很大的壓力,這時候就至關於出現了緩存穿透。

可是,緩存穿透真正要防止的是黑客。

若是一個黑客每次故意查詢一個在緩存內必然不存在的數據,致使每次請求都要去存儲層去查詢,這樣緩存就失去了意義。若是在大流量下數據庫可能掛掉,這也是緩存擊穿。 解決辦法:

  • 一、增長參數校驗
  • 二、從網關層Nginx增長配置項,對單個IP每秒訪問次數超出閾值的拉黑處理
  • 三、Bloom Filter 能很好地防止緩存穿透、他的原理也很簡單的就是利用高效的數據結構和算法快速判斷出你這個Key是否在數據庫,不存在直接return、存在就直接去DB刷新KV再return

利用布隆過濾器來防止緩存擊穿,主要是經過將已存在的緩存放到布隆過濾器中,當黑客訪問不存在的緩存時迅速返回避免緩存及DB掛掉。

至於什麼是布隆過濾器

布隆過濾器本質上布隆過濾器是一種數據結構,比較巧妙的機率型數據結構(probabilistic data structure),特色是高效地插入和查詢,能夠用來告訴你 「某樣東西必定不存在或者可能存在」。

布隆過濾器是由一個很長的bit數組和一系列哈希函數組成的。

數組的每一個元素都只佔1bit空間,而且每一個元素只能爲0或1。

布隆過濾器擁有k個哈希函數,當一個元素加入布隆過濾器時,會使用k個哈希函數對其進行k次計算,獲得k個哈希值,而且根據獲得的哈希值,在位數組中把對應下標的值置位1。

判斷某個數是否在布隆過濾器中,就對該元素進行k次哈希計算,獲得的值在位數組中判斷每一個元素是否都爲1,若是每一個元素都爲1,就說明這個值在布隆過濾器中。

當插入的元素愈來愈多時,當一個不在布隆過濾器中的元素,通過一樣規則的哈希計算以後,獲得的值在位數組中查詢,有可能這些位置由於其餘的元素先被置1了。

因此布隆過濾器存在誤判的狀況,可是若是布隆過濾器判斷某個元素不在布隆過濾器中,那麼這個值就必定不在。

經過這個方法,就能夠有效的防止黑客致使的緩存穿透了。

2、Redis集羣的實現

一、傳統的主從模式

其實不是很傳統,只是我感受全部的集羣都有主從模式orz

主從模式的一個做用是備份數據,這樣當一個節點損壞(指不可恢復的硬件損壞)時,數據由於有備份,能夠方便恢復。 另外一個做用是負載均衡,全部客戶端都訪問一個節點確定會影響Redis工做效率,有了主從之後,查詢操做就能夠經過查詢從節點來完成。

在主從模式中,一個Master能夠有多個Slaves,默認配置下,master節點能夠進行讀和寫,slave節點只能進行讀操做,沒法進行寫操做

若是修改默認配置,可讓slave進行寫,可是這毫無心義,由於寫入的數據不會同步給其餘slave,同時,master節點若是修改了,slave上的數據回立刻被覆蓋

slave節點掛了不影響其餘slave節點的讀和master節點的讀和寫,從新啓動後會將數據從master節點同步過來。master節點掛了之後,不影響slave節點的讀,Redis將再也不提供寫服務,master節點啓動後Redis將從新對外提供寫服務

因此,咱們能夠發現Redis的主從和Zookeeper的主從徹底不同!它居然不會選舉!

這個缺點影響是很大的,尤爲是對生產環境來講,是一刻都不能中止服務的,因此通常的生產壞境是不會單單隻有主從模式的。因此有了下面的sentinel模式。

二、sentinel模式(哨兵模式)

哨兵模式要搭配主從模式來使用,主從不能本身選舉,那咱們就加一個哨兵,當sentinel發現master節點掛了之後,sentinel就會從slave中從新選舉一個master。

哨兵的做用就是監控Redis系統的運行情況。它的功能包括如下兩個。

(1)監控主服務器和從服務器是否正常運行。 
(2)主服務器出現故障時自動將從服務器轉換爲主服務器。
複製代碼

這不就皆大歡喜了嗎?

哨兵的工做方式:

  • 一、每一個Sentinel(哨兵)進程以每秒鐘一次的頻率向整個集羣中的Master主服務器,Slave從服務器以及其餘Sentinel(哨兵)進程發送一個 PING 命令。
  • 二、若是一個實例(instance)距離最後一次有效回覆 PING 命令的時間超過 down-after-milliseconds 選項所指定的值, 則這個實例會被 Sentinel(哨兵)進程標記爲主觀下線(SDOWN)
  • 三、若是一個Master主服務器被標記爲主觀下線(SDOWN),則正在監視這個Master主服務器的全部 Sentinel(哨兵)進程要以每秒一次的頻率確認Master主服務器的確進入了主觀下線狀態 當有足夠數量的 Sentinel(哨兵)進程(大於等於配置文件指定的值)在指定的時間範圍內確認Master主服務器進入了主觀下線狀態(SDOWN), 則Master主服務器會被標記爲客觀下線(ODOWN)
  • 四、在通常狀況下, 每一個 Sentinel(哨兵)進程會以每 10 秒一次的頻率向集羣中的全部Master主服務器、Slave從服務器發送 INFO 命令。 當Master主服務器被 Sentinel(哨兵)進程標記爲客觀下線(ODOWN)時,Sentinel(哨兵)進程向下線的 Master主服務器的全部 Slave從服務器發送 INFO 命令的頻率會從 10 秒一次改成每秒一次。
  • 五、若沒有足夠數量的 Sentinel(哨兵)進程贊成 Master主服務器下線, Master主服務器的客觀下線狀態就會被移除。若 Master主服務器從新向 Sentinel(哨兵)進程發送 PING 命令返回有效回覆,Master主服務器的主觀下線狀態就會被移除。

sentinel模式基本能夠知足通常生產的需求,具有高可用性。可是當數據量過大到一臺服務器存放不下的狀況時,主從模式或sentinel模式就不能知足需求了,這個時候須要對存儲的數據進行分片,將數據存儲到多個Redis實例中,這就是cluster模式。

三、cluster模式

cluster的出現是爲了解決單機Redis容量有限的問題,將Redis的數據根據必定的規則分配到多臺機器。

Redis-Cluster採用無中心結構,它的特色以下:

  • 全部的redis節點彼此互聯(PING-PONG機制),內部使用二進制協議優化傳輸速度和帶寬。

  • 節點的失效是經過集羣中超過半數的節點檢測失效時才生效。

  • 客戶端與redis節點直連,不須要中間代理層.客戶端不須要鏈接集羣全部節點,鏈接集羣中任何一個可用節點便可。

cluster能夠說是sentinel和主從模式的結合體,經過cluster能夠實現主從和master重選功能,因此若是配置三個副本三個分片的話,就需九六個Redis實例。 由於Redis的數據是根據必定規則分配到cluster的不一樣機器的,當數據量過大時,能夠新增機器進行擴容,這種模式適合數據量巨大的緩存要求,當數據量不是很大使用sentinel便可。

引用一張大佬的圖片來直觀展示一下什麼是 Redis-Cluster

每一個請求訪問Redis-Cluster集羣的時候,都會進行一個路由,路由能夠經過Hash(也能夠用別的)來進行隨機分片,可是若是徹底hash的話極可能致使分片們旱的旱死,澇的澇死。,因此,提出了**一致性哈希(自動緩存遷移)+虛擬節點(自動負載均衡)**的方法來解決問題

具體內容你們能夠看看這篇文章,寫的比較詳細,看的也很過癮

www.jianshu.com/p/49c9e03ee… 關於redis的幾件小事(十)redis cluster模式

一致性哈希的原理 :將全部master node落在一個圓環上面,而後,有一個key過來以後。一樣就是hash值,而後會用hash值在圓環對應的各個點上(每一個點都有一個hash值)去對比,看hash值落在那個位置,落在圓環上面之後,就會順時針旋轉去尋找距離本身最近的一個節點,數據的存儲於讀取都在該節點進行。

一致性哈希的優點 :保證了任何一個master宕機,只會影響以前在那個master上面的數據,由於照着順時針走,所有在以前的master上面找不到了,master也宕機了,就會繼續順着順時針走到下一個master節點去。這樣就只會有一部分數據丟失。

3、內存淘汰機制

既然Redis能儲存數據,天然也就要刪除多餘的數據,否則,空間都被佔滿了,新的內容放在哪裏?聰明的程序員提出了三個辦法解決Redis的內存問題。

  • 一、定時刪除
  • 二、惰性刪除——查詢後若是過時就不返回、過時則刪除。這種狀況容易出現不少冗餘數據致使佔用大量空間

一、定時刪除

建立一個定時器,當key設置有過時時間,且過時時間到達時,由定時器任務當即執行對鍵的刪除操做,默認100ms就隨機抽一些key判斷是否過時,過時的話就刪除,用處理器性能換取存儲空間(拿時間換空間)

  • 優勢:節約內存,到時就刪除,快速釋放掉沒必要要的內存佔用

  • 缺點:CPU壓力很大,不管CPU此時負載量多高,均佔用CPU,會影響redis服務器響應時間和指令吞吐量

二、惰性刪除

當Redis中的數據到了過時時間,咱們先不作處理。等下次訪問該數據時進行一次判斷,若是未過時,就正常返回,若是發現數據已過時,馬上刪除,而後返回不存在,用存儲空間換取處理器性能(拿空間換時間)

  • 優勢:節約CPU性能,發現必須刪除的時候才刪除

  • 缺點:內存壓力很大,出現長期佔用內存的數據

不知道你們不知道發現了沒有,大部分的算法,不是時間換空間,就是空間換時間。剛剛發現這個祕密的我簡直驚呆了,這就是人類的終極奧祕之一了,只要咱們知道這個訣竅,就能解決大部分的問題。

三、淘汰機制

在Redis的redis.config文件中還可經過搜索maxmemory-policy來設置淘汰機制

noeviction:當內存使用達到閾值的時候,全部引發申請內存的命令會報錯。
allkeys-lru:在主鍵空間中,優先移除最近未使用的key。
volatile-lru:在設置了過時時間的鍵空間中,優先移除最近未使用的key。
allkeys-random:在主鍵空間中,隨機移除某個key。
volatile-random:在設置了過時時間的鍵空間中,隨機移除某個key。
volatile-ttl:在設置了過時時間的鍵空間中,具備更早過時時間的key優先移除。
複製代碼

結語

Redis篇寫完了,感受Redis真的是有好多內容啊,以前以爲本身貌似全都掌握了同樣,回過頭來,發現仍是有不少不懂~~~~~很開心能在這裏給你們分享個人收穫,我知道個人技術棧比起各位響噹噹的大佬仍是有差距,可是人不努力怎麼知道本身不能夠?但願你們能喜歡個人文章,也但願這篇文章能幫到你們。

同時須要思惟導圖的話,能夠聯繫我,畢竟知識越分享越香!

在這裏插入圖片描述
相關文章
相關標籤/搜索