其中:node
負責輪詢監聽服務器套接字和客戶端套接字,這裏是NIO的機制,因此每一個套接字中間的可讀,可寫狀態沒有阻塞,避免了線程花費時間等待可讀、可寫狀態。多路IO複用器監聽以後生成了各個事件,分爲可讀和可寫兩類並攜帶對應套接字,而後遍歷事件將其push到一個隊列中去,由文件事件分派器去異步處理。redis
它是單線程的,處理一個set命令的業務操做是原子的。文件事件分派器負責根據事件分派到不一樣的事件應答器去處理。這裏分派的邏輯是:算法
包括string,list(提及是List,其實就是JAVA中的有序隊列),hash散列表,set(無序,元素不重複),sortedset(有序,元素不重複,set時須要指定分數,排序根據分數來排)。數據庫
過時刪除有三種狀況:緩存
默認每隔100ms隨機抽取一些設置了過時時間的key刪除。服務器
後面在獲取某個key時,redis會檢查key是否設置了過時時間是否過時,若是過時就刪除,不返回任何東西。網絡
指的是,內存不夠了,redis會運行淘汰部分鍵,這裏根據參數不一樣採起不一樣的策略,其中有:架構
Redis持久化有兩種:RDB機制和AOF機制。其中RDB機制是定時全量在磁盤中備分內存中的數據。AOF機制是以命令日誌的方式在磁盤中備份數據。當master節點宕機重啓後,會根據持久化的數據還原到內存中。若是RDB和AOF被同時開啓,將優先用AOF來進行數據恢復。併發
兩種機制的對比:框架
redis架構模式有兩種,一種是主從架構,一種是rediscluster架構。下面先總結下主從架構。
主從架構相比rediscluster的優勢是能夠讀寫分離,但缺點是不能夠經過加服務器的方式橫向擴展redis內存空間。
主從架構由1個master節點,n個slave節點構成,master節點負責寫,slave節點負責讀。
全量複製場景通常是在slave第一次啓動或者長時間離線致使數據和master節點相差過大的時候產生的。其過程是master節點後臺開啓線程將內存中最新的數據全量生成一份rdb文件,同時這段時間內寫入命令也緩存在內存中。slave收到rdb文件以後先持久化到本身磁盤,而後寫入本身的緩存,而後master把前面內存中緩存的寫命令發送給slave節點,slave再執行一遍寫命令。這個過程叫作全量複製。
注:
異步複製指的是後續節點保持同步是經過異步複製完成的,master節點收到寫命令,寫完成後返回客戶端寫成功,而後異步的複製給slave節點。
要保證主從架構的高可用必須配置哨兵集羣,故障轉移是經過哨兵架構來完成的,哨兵是不一樣的進程,也是一個集羣。下面主要介紹下哨兵架構。
其內部通訊機制基於Redis的發佈訂閱,一個哨兵集羣會訂閱redis同一個channel。經過同一個channel交換信息。
sdown-主觀宕機,描述的master節點狀態被部分哨兵節點認爲宕機,可是沒有真正觸發到主從切換的條件。
odown-客觀宕機,描述的是master節點狀態觸發了主從切換的條件。
注:當哨兵節點Ping master節點若是延遲超過了is-master-down-agter-millseconds指定毫秒數以後,該節點就認爲Master節點宕機
配置文件的quorum參數,quorum配置的是執行主從切換須要多少個哨兵節點認爲master節點宕機。
當判斷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節點的信息。
因爲哨兵和redis機器是分不開的,哨兵集羣做爲redis高可用保證,自身也應該保證是高可用的。因此通常至少須要3個哨兵節點,由於若是隻有2個哨兵節點,掛的master節點的機器上又有哨兵進程,那麼這時哨兵節點就只剩下兩個,而2的majority數又是2,這時是沒法完成主備切換的。
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節點讀。
多master節點,多slave。
數據分佈算法通常有三種經常使用算法:
在集羣內部存在的元數據包括hashslot和Node的映射表、master和slave關係、、故障信息、節點的增長和移除等等是如何通訊的勒?redis使用的是Gossip協議來通訊維護這些元數據,每一個節點將開放兩個端口,一個端口是面向客戶端提供服務的端口,另外一個端口端口號是服務端口+10000集羣內部節點通訊的端口,稱爲cluter bus集羣總線。Gossip將消息分爲4種類型:
redisCluster的主備切換相似哨兵模式,其主要流程是:
使用原生命令與Redis集羣交互主要一種是隨意鏈接一個節點,在寫入數據時,由該節點計算key的hashSlot值,若是該節點不是對應key的處理節點,該請求將重定向到對應節點。另外,在命令上能夠加入hashtag手動指定hashslot,如:set myke1:{hash tag} 值,這樣免除了節點實例計算hashSlot.
在Jedis框架裏本地維護一個hashslot映射表緩存,工做流程失在rediscluster初始化的時候隨機找一個節點初始化hashslot映射表到本地,後面發送請求的時候根據映射表尋找節點,若是節點返回Moved(表示進行了重定向),就以該節點元數據再更新一下本身的緩存。另外,若是hashslot正在遷移,將返回ask(表示hashslot正在遷移中),jedicluster會重定向到目標節點嘗試請求,但Jedisscluster不會進行更新映射表。
指的是:
讀的時候,先讀緩存,緩存沒有的話,讀數據庫,取出數據後放入緩存,同時返回響應。
更新的時候,先更新數據庫,在刪除緩存
這裏主要涉及兩個問題,集中在更新的時候:
第一是爲何是刪除緩存而不是更新,由於若是更新緩存會破壞緩存意義,更新並不表明這個數據是熱點訪問數據,若是更新可能會破壞緩存的LRU特性,更新緩存應該以訪問的動做爲驅動。另外有時候數據庫數據關聯的緩存是通過必定計算才關聯的,若是須要去更新緩存可能涉及的計算比較複雜。
第二是爲何是先更新數據庫,在刪除緩存,由於這是爲了不緩存與數據庫雙寫不一致,如:若修改數據數據庫事務還沒提交,可是已經把緩存從redis中刪除,此時來了個讀請求,會把舊的數據刷到緩存裏面,這樣就致使了緩存中的數據直到下一次修改數據庫以前確定是與數據庫不一致的。
讀寫併發致使,讀的時候沒讀到緩存,同時寫的時候刪除了緩存但還沒來得及更新數據庫,致使讀的時候去更新緩存更新成了舊的數據。
先更新數據庫,在刪除緩存,同時用redis主從提升redis可用性,避免redis刪除操做失敗,另外若是是讀寫分離的狀況能夠用阿里的canel組件,監遵從庫的binlog,再去消費消息刪除緩存。
因爲緩存宕機了,大量請求沒有命中緩存,數據庫接收大量請求壓力
大量惡意攻擊(請求數據庫沒有的數據)請求,直接擊穿了緩存,每次都去走數據庫。
一是要作好事前的參數校驗,高級一點就用布隆過濾器(至關於全部數據都用必定算法放在一個字典中,經過數據hash只判斷在字典中存不存在)。 二是採起暴力一點的辦法沒查到就寫一個空值到緩存裏去(注意設置過時時間),這種有必定限制效果,由於惡意攻擊方必須耗費精力準備足夠大的參數字典來攻擊。
短期內針對一個key的更新併發,致使緩存更新沒有保證順序,因爲順序錯亂,可能緩存的數據就錯了。
使用分佈式鎖,能夠基於zookeeper或redis本身,串行化某個key的更新操做,拿到鎖以後,判斷這把鎖的時間戳(value)是否比本身的要新,新的話就不更新緩存了。反之將本身的時間戳打上這把鎖,並更新緩存。