1. 程式人生 > 資料庫 >一週一箇中間件-redis

一週一箇中間件-redis

前言

redis作為記憶體資料庫,具有高速讀取效率。因為單執行緒原因。避免多執行緒之間的切換時間。讀取速率快。

基礎知識

  • redis資料結構

    • String 字串
    • List 陣列
    • hash 是一種鍵值對,儲存的時候必須成對的出現。redis的key對應的value,這個value是map<string,Object>結構
    • Set set陣列不重複
    • ZSet sort set 陣列 有序陣列
    • HyperLogLog 布隆過濾器
    • GEOHash 將二維資料對映到一維資料,可以做“附近的人”業務邏輯
  • 主觀下線

所謂主觀下線(Subjectively Down, 簡稱 SDOWN)指的是單個Sentinel例項對伺服器做出的下線判斷,即單個sentinel認為某個服務下線(有可能是接收不到訂閱,之間的網路不通等等原因)。

主觀下線就是說如果伺服器在down-after-milliseconds給定的毫秒數之內, 沒有返回 Sentinel 傳送的 PING 命令的回覆, 或者返回一個錯誤, 那麼 Sentinel 將這個伺服器標記為主觀下線(SDOWN )。
sentinel會以每秒一次的頻率向所有與其建立了命令連線的例項(master,從服務,其他sentinel)發ping命令,通過判斷ping回覆是有效回覆,還是無效回覆來判斷例項時候線上(對該sentinel來說是“主觀線上”)。
sentinel配置檔案中的down-after-milliseconds設定了判斷主觀下線的時間長度,如果例項在down-after-milliseconds毫秒內,返回的都是無效回覆,那麼sentinel回認為該例項已(主觀)下線,修改其flags狀態為SRI_S_DOWN。如果多個sentinel監視一個服務,有可能存在多個sentinel的down-after-milliseconds配置不同,這個在實際生產中要注意。

  • 客觀下線

客觀下線(Objectively Down, 簡稱 ODOWN)指的是多個 Sentinel 例項在對同一個伺服器做出 SDOWN 判斷, 並且通過 SENTINEL is-master-down-by-addr 命令互相交流之後, 得出的伺服器下線判斷,然後開啟failover。
客觀下線就是說只有在足夠數量的 Sentinel 都將一個伺服器標記為主觀下線之後, 伺服器才會被標記為客觀下線(ODOWN)。
只有當master被認定為客觀下線時,才會發生故障遷移。
當sentinel監視的某個服務主觀下線後,sentinel會詢問其它監視該服務的sentinel,看它們是否也認為該服務主觀下線,接收到足夠數量(這個值可以配置)的sentinel判斷為主觀下線,既任務該服務客觀下線,並對其做故障轉移操作。

sentinel通過傳送 SENTINEL is-master-down-by-addr ip port current_epoch runid,(ip:主觀下線的服務id,port:主觀下線的服務埠,current_epoch:sentinel的紀元,runid:*表示檢測服務下線狀態,如果是sentinel 執行id,表示用來選舉領頭sentinel)來詢問其它sentinel是否同意服務下線。
一個sentinel接收另一個sentinel發來的is-master-down-by-addr後,提取引數,根據ip和埠,檢測該服務時候在該sentinel主觀下線,並且回覆is-master-down-by-addr,回覆包含三個引數:down_state(1表示已下線,0表示未下線),leader_runid(領頭sentinal id),leader_epoch(領頭sentinel紀元)。
sentinel接收到回覆後,根據配置設定的下線最小數量,達到這個值,既認為該服務客觀下線。

  • 節點資料同步

rdb(redis database) 在指定時間間隔內,將記憶體中的資料集快照寫入磁碟,也就是Snapshot快照,它恢復時是將快照檔案直接讀到記憶體中,來達到恢復資料的。
aop (append only file)以日誌的形式記錄Redis每一個寫操作,將Redis執行過的所有寫指令記錄下來(讀操作不記錄),只許追加檔案不可以改寫檔案,redis啟動之後會讀取appendonly.aof檔案來實現重新恢復資料,完成恢復資料的工作。預設不開啟,需要將redis.conf中的appendonly no改為yes啟動Redis。

參考文獻

windows下載redis

linux下載redis

Redis哨兵模式
Redis哨兵模式(sentinel)學習總結及部署記錄(主從複製、讀寫分離、主從切換

叢集架構

主從模式

一個master可以有多個slaves,但是主掛掉之後,slave不能變成master

sentinel模式(哨兵模式)

sentinel模式是整合在redis中。sentinel系統可以監控一個或者多個redis master服務。以及這些master服務的所有從服務。當某個master服務下線時,自動將該master下的某個從服務升級為master服務替代已下線的master服務繼續處理請求。
一般建議sentinel採取奇數臺,防止某一臺sentinel無法連線到master導致誤切換。

* 當sentinel發現master節點掛了以後,sentinel就會從slave中重新選舉一個master。
* sentinel模式是建立在主從模式的基礎上,如果只有一個Redis節點,sentinel就沒有任何意義
* 當master節點掛了以後,sentinel會在slave中選擇一個做為master,並修改它們的配置檔案,其他slave的配置檔案也會被修改,比如slaveof屬性會指向新的master
* 當master節點重新啟動後,它將不再是master而是做為slave接收新的master節點的同步資料
* sentinel因為也是一個程序有掛掉的可能,所以sentinel也會啟動多個形成一個sentinel叢集
* 當主從模式配置密碼時,sentinel也會同步將配置資訊修改到配置檔案中,不許要擔心。
* 一個sentinel或sentinel叢集可以管理多個主從Redis。
* sentinel最好不要和Redis部署在同一臺機器,不然Redis的伺服器掛了以後,sentinel也掛了
* sentinel監控的Redis叢集都會定義一個master名字,這個名字代表Redis叢集的master Redis
* 根據slaveof x.x.x.x 6379 從庫設定主庫連結 可以做到主從複製
* 通過sentinel,設定的sentinel monitor mymaster 47.104.87.10 6379 2 向主節點獲取所有主從節點資訊,選中主節點。
* Master-Slave切換後,master_redis.conf、slave_redis.conf和sentinel.conf的內容都會發生改變,即master_redis.conf中會多一行slaveof的配置,sentinel.conf的監控目標會隨之調換。

操作
當使用sentinel模式的時候,客戶端就不要直接連線Redis,而是連線sentinel的ip和port,由sentinel來提供具體的可提供服務的Redis實現,這樣當master節點掛掉以後,sentinel就會感知並將新的master節點提供給使用者。

sentinel工作模式

  • 每個Sentinel以每秒鐘一次的頻率向它所知的Master,Slave以及其他 Sentinel 例項傳送一個PING命令。
  • 如果一個例項(instance)距離最後一次有效回覆PING命令的時間超過 own-after-milliseconds 選項所指定的值,則這個例項會被Sentinel標記為主觀下線。
  • 如果一個Master被標記為主觀下線,則正在監視這個Master的所有 Sentinel 要以每秒一次的頻率確認Master的確進入了主觀下線狀態。
  • 當有足夠數量的Sentinel(大於等於配置檔案指定的值)在指定的時間範圍內確認Master的確進入了主觀下線狀態,則Master會被標記為客觀下線。
    在一般情況下,每個Sentinel 會以每10秒一次的頻率向它已知的所有Master,Slave傳送 INFO 命令。
  • 當Master被Sentinel標記為客觀下線時,Sentinel 向下線的 Master 的所有Slave傳送 INFO命令的頻率會從10秒一次改為每秒一次。
  • 若沒有足夠數量的Sentinel同意Master已經下線,Master的客觀下線狀態就會被移除。 若 Master重新向Sentinel 的PING命令返回有效回覆,Master的主觀下線狀態就會被移除。

sentinel內部3個定時任務

  • 每10秒每個sentinel會對master和slave執行info命令,這個任務達到兩個目的 a)發現slave節點 b)確認主從關係
  • 每2秒每個sentinel通過master節點的channel交換資訊(pub/sub)。master節點上有一個釋出訂閱的頻道(sentinel:hello)。sentinel節點通過sentinel:hello頻道進行資訊交換(對節點的”看法”和自身的資訊),達成共識。
  • 每1秒每個sentinel對其他sentinel和redis節點執行ping操作(相互監控),這個其實是一個心跳檢測,是失敗判定的依據。

Redis Cluser模式

  • 中心架構
  • 資料按照slot儲存分佈在多個節點,節點間資料共享,可動態調整資料分佈
  • 可擴充套件性,可線性擴充套件到1000個節點,節點可動態新增或刪除
  • 高可用性,部分節點不可用時,叢集仍可用。通過增加Slave做standby資料副本,能夠實現故障自動 failover,節點之間通過gossip協議交換狀態資訊,用投票機制完成Slave到Master的角色提升.
  • 槽位

redis cluster一共有16384個槽。編號為0,1,2,3…16383。這個槽是虛擬的槽位。並不是真實存在的。每個Master節點都會負責一部分槽。當redis的某些key值被對映到某些master負責的槽。至於那個Master節點負責那個槽。1)可以由使用者指定,2)可以在初始化的時候自動生成(redis-trib.rb指令碼)。Master才有擁有槽的所有權,slave只負責槽的使用,沒有所有權。
Master節點維護著16384/8位元組的位序列。Master節點用bit來標識對於某個槽自己是否擁有。叢集同時還維護著槽到叢集節點的對映,是由長度為16384型別為節點的陣列實現的,槽編號為陣列的下標,陣列內容為叢集節點,這樣就可以很快地通過槽編號找到負責這個槽的節點。位序列這個結構很精巧,即不浪費儲存空間,操作起來又很便捷。

  • 鍵空間分佈演算法

通過雜湊演算法再加上取模運算可以將一個值固定地對映到某個區間,在這裡,這個區間叫做slots,區間由連續的slot組成。在Redis Cluster中,我們擁有16384個slot,這個數是固定的,我們儲存在Redis Cluster中的所有的鍵都會被對映到這些slot中.
HASH_SLOT = CRC16(key) mod 16384

  • 鍵雜湊標籤原理

這是是使用者將一批鍵存放在同一個槽中的實現方法。使用者需要按照指定規則生成key。
規則: abc{userId}def和ghi{userId}jkl
redis在計算槽編號的時候只會獲取{}之間的字串進行槽編號計算,這樣由於上面兩個不同的鍵,{}裡面的字串是相同的,因此他們可以被計算出相同的槽,

  • 重新分片

當叢集Master節點出現問題。出現槽和節點映射出現問題。但是槽和鍵的對映關係不變。
MIGRATING狀態
預備遷移槽的時候槽的狀態首先會變為MIGRATING狀態.當客戶端請求的某個Key所屬的槽處於MIGRATING狀態的時候,影響有下面幾條:

如果Key存在則成功處理

如果Key不存在,則返回客戶端ASK,僅當這次請求會轉向另一個節點,並不會重新整理客戶端中node的對映關係,也就是說下次該客戶端請求該Key的時候,還會選擇MasterA節點

如果Key包含多個命令,如果都存在則成功處理,如果都不存在,則返回客戶端ASK,如果一部分存在,則返回客戶端TRYAGAIN,通知客戶端稍後重試,這樣當所有的Key都遷移完畢的時候客戶端重試請求的時候回得到ASK,然後經過一次重定向就可以獲取這批鍵

IMPORTING狀態預備將槽從MasterA節點遷移到MasterB節點的時候,槽的狀態會首先變為IMPORTING。IMPORTING狀態的槽對客戶端的行為有下面一些影響

正常命令會被MOVED重定向,如果是ASKING命令則命令會被執行,從而Key沒有在老的節點已經被遷移到新的
節點的情況可以被順利處理;

如果Key不存在則新建;

沒有ASKING的請求和正常請求一樣被MOVED,這保證客戶端node對映關係出錯的情況下不會發生寫錯;

總結redis資料同步:

  1. 當一個從資料庫啟動時,會向主資料庫傳送sync命令,
  2. 主資料庫接收到sync命令後會開始在後臺儲存快照(執行rdb操作),並將儲存期間接收到的命令快取起來
  3. 當快照完成後,redis會將快照檔案和所有快取的命令傳送給從資料庫。
  4. 從資料庫收到後,會載入快照檔案並執行收到的快取的命令。

安裝redis

  • sentinel模式安裝
  • redis cluster模式安裝

redis快取失效機制

redis對快取失效二種方法
客戶端去查詢時進行的消極處理
主執行緒定時主動處理

redis的失效快取機制需要從EXPIRE命令來說,EXPIRE允許使用者為某個key指定超時時間,當超過這個時間之後key對應的值會被清除。redis資料庫中有二個位置,dict位置儲存正常資料,expires使用儲存關聯過期時間key的。
redis查詢機制從expires中查詢key的過期時間,如果不存在說明對應key沒有設定過期時間,直接返回如果是slave機器,則直接返回,因為Redis為了保證資料一致性且實現簡單,將快取失效的主動權交給Master機器,
slave機器沒有許可權將key失效。如果當前是Master機器,且key過期,則master會做兩件重要的事情:1)將刪除命令寫入AOF檔案。2)通知Slave當前key失效,可以刪除了。3)master從本地的字典中將key對於的值刪除。

redis服務端定時去檢查失效的快取。使用的淘汰策略
redis定時時間:配置項 hz預設為10,就是CPU空閒時每秒執行10次,但是每次執行的時間都不超過cpu的25%時間,hz=1,一次清理最大250ms,hz=10,每次清理最大25ms。另外一個配置maxmemory最大值,當記憶體超過maxmemory限定時,就會觸發主動清理策略。每隔一段時間隨機抽取一部分key來檢查清除,大於某個閾值繼續檢查清除,小於某個閾值就結束。
結論:因為演算法採用隨機key判斷,故不能清理完所有過期key
定期刪除漏掉了很多過期key,然後你也沒及時去查,也就沒走惰性刪除.
記憶體淘汰策略

  1. noeviction:返回錯誤當記憶體限制達到並且客戶端嘗試執行會讓更多記憶體被使用的命令(大部分的寫入指令,但DEL和幾個例外)
  2. allkeys-lru: 嘗試回收最少使用的鍵(LRU),使得新新增的資料有空間存放。
  3. volatile-lru: 嘗試回收最少使用的鍵(LRU),但僅限於在過期集合的鍵,使得新新增的資料有空間存放。
  4. allkeys-random: 回收隨機的鍵使得新新增的資料有空間存放。
  5. volatile-random: 回收隨機的鍵使得新新增的資料有空間存放,但僅限於在過期集合的鍵。
  6. volatile-ttl: 回收在過期集合的鍵,並且優先回收存活時間(TTL)較短的鍵,使得新新增的資料有空間存放。

redis分散式鎖

redis內部命令INCR
將要加鎖的key執行INCR操作進行加1.如果返回的不是大於1,就代表已經加鎖了,如果等於1,代表加鎖成功。

  • SETNX內部命令

如果key不存在,就將key設定value.如果成功,就加鎖成功,如果key已經成功,代表已經被加鎖,SETNX不做任何操作。

  • SET內部命令

帶有過期時間限制的鎖。
Redisson可以通過看門狗模式來進行續期。
Redis分散式鎖的缺點
在redis cluster和主從架構,客戶端1對原先的redis master加鎖,Master宕機後,新的Slave變成Master,客戶端2再對新的Master加鎖。導致多個客戶端對一個分散式鎖完成加鎖。

redis客戶端

  • jedis

輕量,簡潔。支援連線池。支援pipelining,事務,LUA Scripting。Redis Sentinel,Redis Cluster。不支援讀寫分離。需要自己實現。

@Test
public void test4combPipelineTrans() {
    jedis = new Jedis("localhost");
    long start = System.currentTimeMillis();
    // 開啟Redis管道,開啟事務
    Pipeline pipeline = jedis.pipelined();
    pipeline.multi();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("" + i, "" + i);
    }
     // 執行Redis管道事務
    pipeline.exec();
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    // 關閉Redis事務
    jedis.disconnect();
}
  • Redisson

Redisson內部提供一個監控鎖的看門狗,每十秒檢查一下,它的作用是在Redisson例項被關閉前,不斷延長鎖的有效期,預設情況下,看門狗的檢查鎖的超時時間是30秒,也可以通過Config.lockWatchdog.timeout來自定義設定。預設時間加鎖時間是30秒,當任務執行到30-10=20秒,就會進行一次續期。把鎖重置到30秒。伺服器宕機後,定時任務跑步了,30秒後自動解開了

Redisson還可以通過加鎖的方式提供leasetime的引數來指定加鎖時間,超過時間鎖自動解開了。

每次加鎖加鎖次數加1,每次減鎖對鎖進行減1,當發現加鎖次數位0時,客戶端釋放鎖成功。這就是分散式鎖開源Redisson框架的實現原理。
基於netty實現,採用非阻塞IO,支援非同步請求,支援連線池。支援pipelining, LUA Scripting,Redis Sentinel,Redis Cluster.Redisson建議LUA Scripting代替事務。支援讀寫分離,支援讀寫負載均衡。在主從複製,和Redis Cluster架構都可以使用。