Redis 三種集羣策略

前言

  Redis 是單線程的,但是一般的作爲緩存使用的話,速度已經足夠使用。
  官方有一個簡單測試:測試完成 50 個併發執行 100000 個請求,設置和獲取的值是一個 256 字節字符串。結果:讀的速度 110000次/s,寫的速度81000次/s。

  不過對於訪問量特別大的服務來說,還是稍有不足。這時就需要考慮搭建集羣。

Redis 主要提供三種集羣策略

  1. 主從複製

  2. 集羣

  3. 哨兵

 

一、主從複製

  在主從複製中,數據庫分爲倆類,主數據庫(master)和從數據庫(slave)。

 

主從複製特點:

  1. 主數據庫可以進行讀寫操作,當讀寫操作導致數據變化時會自動將數據同步給從數據庫

  2. 從數據庫一般都是隻讀,並且接收主數據庫同步過來的數據

  3. 一個 Master 可以擁有多個 Slave,但是一個 Slave 只能對應一個 Master 

 

工作機制

  1. Slave 從節點服務啓動並連接到 Master 之後,主動發送一個 SYNC 命令。Master 服務主節點收到同步命令後將啓動後臺存盤進程,同時收集所有接收到的用於修改數據集的命令,在後臺進程執行完畢後,Master 將傳送整個數據庫文件到 Slave,以完成一次完全同步。而 Slave 從節點服務在接收到數據庫文件數據之後將其存盤並加載到內存中。此後,Master 主節點繼續將所有已經收集到的修改命令,和新的修改命令依次傳送給 Slave,Slave 將在本次執行這些數據修改命令,從而達到最終的數據同步。

  2. 複製初始化後,Master 每次接收到的寫命令都會同步發送給 Slave,保證主從數據一致性。

  3. 如果 Master 和 Slave 之間的鏈接出現斷連現象,Slave 可以自動重連 Master,但是在連接成功之後,一次完全同步將被自動執行。

 

主從配置

  Redis 默認是主數據,所以 Master 無需配置,只需要修改 Slave 的配置即可。
  設置需要連接的 Master 的ip端口

slaveof 192.168.0.107 6379


如果 Master 設置了密碼。需要配置:

masterauth master-password


連接成功進入命令行後,可以通過以下命令行查看連接該數據庫的其他庫信息:

info replication

 

優點

  1. 同一個 Master 可以同步多個 Slaves。

  2. Slave 同樣可以接受其它 Slaves 的連接和同步請求,這樣可以有效的分載 Master 同步壓力。因此我們可以將 Redis 的 Replication 架構視爲圖結構。

  3. Master Server是 以非阻塞的方式爲 Slaves 提供服務。所以在 Master-Slave 同步期間,客戶端仍然可以提交查詢或修改請求。

  4. Slave Server 同樣是以非阻塞的方式完成數據同步。在同步期間,如果有客戶端提交查詢請求,Redis 則返回同步之前的數據

  5. 爲了分載 Master 的讀操作壓力,Slave 服務器可以爲客戶端提供只讀操作的服務,寫服務仍然必須由 Master 來完成。即便如此,系統的伸縮性還是得到了很大的提高。

  6. Master可 以將數據保存操作交給 Slaves 完成,從而避免了在Master中要有獨立的進程來完成此操作。

  7. 支持主從複製,主機會自動將數據同步到從機,可以進行讀寫分離。

缺點

 

  1. Redis 不具備自動容錯和恢復功能,主機從機的宕機都會導致前端部分讀寫請求失敗,需要等待機器重啓或者手動切換前端的 IP 才能恢復。

  2. 主機宕機,宕機前有部分數據未能及時同步到從機,切換 IP 後還會引入數據不一致的問題,降低了系統的可用性。

  3. Redis 的主從複製採用全量複製,複製過程中主機會 fork 出一個子進程對內存做一份快照,並將子進程的內存快照保存爲文件發送給從機,這一過程需要確保主機有足夠多的空餘內存。若快照文件較大,對集羣的服務能力會產生較大的影響,而且複製過程是在從機新加入集羣或者從機和主機網絡斷開重連時都會進行,也就是網絡波動都會造成主機和從機間的一次全量的數據複製,這對實際的系統運營造成了不小的麻煩。
  4. Redis 較難支持在線擴容,在集羣容量達到上限時在線擴容會變得很複雜。爲避免這一問題,運維人員在系統上線時必須確保有足夠的空間,這對資源造成了很大的浪費。
  5. 其實 Redis 的主從模式很簡單,在實際的生產環境中是很少使用的,我也不建議在實際的生產環境中使用主從模式來提供系統的高可用性,之所以不建議使用都是由它的缺點造成的,在數據量非常大的情況,或者對系統的高可用性要求很高的情況下,主從模式也是不穩定的。

 

二、哨兵

  該模式是從 Redis 的 2.6 版本開始提供的,但是當時這個版本的模式是不穩定的,直到 Redis 的 2.8 版本以後,這個哨兵模式才穩定下來,無論是主從模式,還是哨兵模式,這兩個模式都有一個問題,不能水平擴容,並且這兩個模式的高可用特性都會受到Master主節點內存的限制。

哨兵的作用

  監控 Redis 系統的運行狀況,功能如下

  1. 監控主從數據庫是否正常運行。

  2. master 出現故障時,自動將它的其中一個 slave 轉化爲 master。

  3. master 和 slave 服務器切換後,master 的 redis.conf、slave 的 redis.conf 和 sentinel.conf 的配置文件的內容都會發生相應的改變,即 master主服務器的 redis.conf 配置文件中會多一行 slaveof 的配置,sentinel.conf 的監控目標會隨之調換。

  4. 當被監控的某個 Redis 節點出現問題時, 哨兵(sentinel) 可以通過 API 向管理員或者其他應用程序發送通知。

  5. 多哨兵配置的時候,哨兵之間也會自動監控。

  6. 多個哨兵可以監控同一個 Redis 。

工作機制

  1. 哨兵進程啓動時會讀取配置文件的內容,通過sentinel monitor master-name ip port quorum查找到master的ip端口。一個哨兵可以監控多個master數據庫,只需要提供多個該配置項即可。

  2. 配置文件還定義了與監控相關的參數,比如master多長時間無響應即即判定位爲下線。另外,搜索公衆號技術社區後臺回覆“思維”,獲取一份驚喜禮包。

  3. 哨兵啓動後,會與要監控的master建立倆條連接:
    3.1 一條連接用來訂閱master的sentinel:hello頻道,並獲取其他監控該master的哨兵節點信息
    3.2 另一條連接定期向master發送INFO等命令獲取master本身的信息

  4. 與master建立連接後,哨兵會執行三個操作,這三個操作的發送頻率都可以在配置文件中配置:
    4.1 定期向master和slave發送INFO命令
    4.2 定期向master和slave的sentinel:hello頻道發送自己的信息
    4.3 定期向master、slave和其他哨兵發送PING命令
    這三個操作的意義非常重大,發送INFO命令可以獲取當前數據庫的相關信息從而實現新節點的自動發現。所以說哨兵只需要配置master數據庫信息就可以自動發現其slave信息。獲取到slave信息後,哨兵也會與slave建立倆條連接執行監控。通過INFO命令,哨兵可以獲取主從數據庫的最新信息,並進行相應的操作,比如角色變更等。

  5. 接下來哨兵向主從數據庫的sentinel:hello頻道發送信息,並與同樣監控這些數據庫的哨兵共享自己的信息,發送內容爲哨兵的ip端口、運行id、配置版本、master名字、master的ip端口還有master的配置版本。這些信息有以下用處:
    5.1 其他哨兵可以通過該信息判斷髮送者是否是新發現的哨兵,如果是的話會創建一個到該哨兵的連接用於發送ping命令。
    5.2 其他哨兵通過該信息可以判斷master的版本,如果該版本高於直接記錄的版本,將會更新

  6. 當實現了自動發現slave和其他哨兵節點後,哨兵就可以通過定期發送ping命令定時監控這些數據庫和節點有沒有停止服務。發送頻率可以配置,但是最長間隔時間爲1s,可以通過sentinel down-after-milliseconds mymaster 600設置。

  7. 如果被ping的數據庫或者節點超時未回覆,哨兵認爲其主觀下線。如果下線的是master,哨兵會向其他哨兵點發送命令詢問他們是否也認爲該master主觀下線。如果一個master主服務器被標記爲主觀下線(SDOWN),則正在監視這個Master主服務器的所有 Sentinel(哨兵)進程要以每秒一次的頻率確認Master主服務器的確進入了主觀下線狀態。如果達到一定數目(即配置文件中的quorum)投票,哨兵會認爲該master已經客觀下線(ODOWN),並選舉領頭的哨兵節點對主從系統發起故障恢復。

  8. 如上文所說,哨兵認爲master客觀下線後,故障恢復的操作需要由選舉的領頭哨兵執行,選舉採用Raft算法:
    8.1 發現master下線的哨兵節點(我們稱他爲A)向每個哨兵發送命令,要求對方選自己爲領頭哨兵
    8.2 如果目標哨兵節點沒有選過其他人,則會同意選舉A爲領頭哨兵
    8.3 如果有超過一半的哨兵同意選舉A爲領頭,則A當選
    8.4 如果有多個哨兵節點同時參選領頭,此時有可能存在一輪投票無競選者勝出,此時每個參選的節點等待一個隨機時間後再次發起參選請求,進行下一輪投票精選,直至選舉出領頭哨兵
    8.5 選出領頭哨兵後,領頭者開始對進行故障恢復,從出現故障的master的從數據庫slave中挑選一個來當選新的master,選擇規則如下:
    8.5.1 所有在線的slave中選擇優先級最高的,優先級可以通過slave-priority配置
    8.5.2 如果有多個最高優先級的slave,則選取複製偏移量最大(即複製越完整)的當選
    8.5.3 如果以上條件都一樣,選取id最小的slave

  9. 挑選出需要繼任的slaver後,領頭哨兵向該數據庫發送命令使其升格爲master,然後再向其他slave發送命令接受新的master,最後更新數據。將已經停止的舊的master更新爲新的master的從數據庫,使其恢復服務後以slave的身份繼續運行。

哨兵配置

  哨兵配置的配置文件爲sentinel.conf,設置主機名稱,地址,端口,以及選舉票數即恢復時最少需要幾個哨兵節點同意。只要配置需要監控的master就可以了,哨兵會監控連接該master的slave。

sentinel monitor mymaster 192.168.0.107 6379 1

啓動哨兵節點

redis-server sentinel.conf --sentinel &

'''
出現以下類似信息即啓動哨兵成功
3072:X 12 Apr 22:40:02.554 ### Sentinel runid is e510bd95d4deba3261de72272130322b2ba650e7
3072:X 12 Apr 22:40:02.554 ### +monitor master mymaster 192.168.0.107 6379 quorum 1
3072:X 12 Apr 22:40:03.516 * +slave slave 192.168.0.108:6379 192.168.0.108 6379 @ mymaster 192.168.0.107 6379
3072:X 12 Apr 22:40:03.516 * +slave slave 192.168.0.109:6379 192.168.0.109 6379 @ mymaster 192.168.0.107 6379
'''

可以在任何一臺服務器上查看指定哨兵節點信息:

bin/redis-cli -h 192.168.0.110 -p 26379 info Sentinel


控制檯輸出哨兵信息

redis-cli -h 192.168.0.110 -p 26379 info Sentinel

 

三、集羣

特點

  3.0 版本之前的 Redis 是不支持集羣的,那個時候,Redis 如果想要集羣的話,就需要一箇中間件,然後這個中間件負責將需要存入 Redis 中的數據的 key 通過一套算法計算得出一個值。然後根據這個值找到對應的 Redis 節點,將這些數據存在這個 Redis 的節點中。在取值的時候,同樣先將 key 進行計算,得到對應的值,然後就去找對應的 Redis 節點,從對應的節點中取出對應的值。

  這樣做有很多不好的地方,比如說計算都需要在系統中去進行,會增加系統的負擔。還有就是這種集羣模式下,某個節點掛掉,其他的節點無法知道。而且也不容易對每個節點進行負載均衡。

  從 Redis 3.0版本開始支持 redis-cluster 集羣。redis-cluster 採用無中心結構,每一個節點都保存有這個集羣所有主節點以及從節點的信息,及集羣狀態,每個節點都和其他節點連接。所以 redis-cluster 是一種服務端分片技術。

  1. 每個節點都和 n-1 個節點通信,被稱爲集羣總線(cluster bus)。它們使用特殊的端口號,即對外服務端口號加 10000。要維護好這個集羣的每個節點信息,不然會導致整個集羣不可用,其內部採用特殊的二進制協議優化傳輸速度和帶寬。

  2. redis-cluster 把所有的物理節點映射到 [0,16383]slot(槽)上,cluster 負責維護 node--slot--value。

  3. 集羣預先給所有節點分好 16384 個桶,每個節點得到部分桶,當需要在 Redis 集羣中插入數據時,根據 CRC16(KEY) mod 16384 的值,決定將一個 key放 到哪個桶中。

  4. 客戶端與 Redis 節點直連,不需要連接集羣所有的節點,連接集羣中任何一個可用節點即可。整個 cluster 被看做是一個整體,客戶端可連接任意一個節點進行操作,當客戶端操作的 key 沒有分配在該節點上時,Redis 會返回轉向指令,指向正確的節點。

  5. redis-trib.rb 腳本(rub 語言)爲集羣的管理工具,比如自動添加節點,規劃槽位,遷移數據等一系列操作。

  6. 節點的 fail 是通過集羣中超過半數的節點檢測失效時才生效。集羣節點之間通過互相的 ping-pong 判斷是否可以連接上。如果有一半以上的節點去 ping 一個節點的時候沒有迴應,集羣就認爲這個節點宕機,然後去連接它的備用節點。如果某個節點和所有從節點全部掛掉,集羣就進入 fail 狀態,也可以理解成集羣的 slot 映射 [0-16383] 不完整時進入 fail 狀態。如果有一半以上的主節點宕機,那麼無論這些節點有沒有從節點,集羣同樣進入fail狀態。這就是redis的投票機制。

  7. 爲了增加集羣的可訪問性,官方推薦的方案是將 node 配置成主從結構,即一個 master 主節點,掛 n 個 slave 從節點。如果主節點失效,redis cluster 會根據選舉算法從 slave 節點中選擇一個上升爲 master 節點,整個集羣繼續對外提供服務。

 

配置

  1. Redis 集羣依賴 ruby,需安裝 ruby 環境,ruby 版本需高於 2.2

    yum install ruby
    yum install rubygems
    gem install redis
  2. 修改配置文件

    bind 192.168.0.107
    ### 配置端口
    port 6380
    ### 配置快照保存路徑,6個節點配置不同路徑
    dir /usr/local/redis-cluster/6380/
    ### 開啓集羣
    cluster-enabled yes
    ### 爲節點設置不同的工作目錄,6個節點配置不同目錄
    cluster-config-file nodes-6380.conf
    ### 集羣失效時間
    cluster-node-timeout 15000
  3. 開啓集羣中的所有節點
    redis-service …/6380/redis.conf
    redis-service …/6381/redis.conf
    redis-service …/6382/redis.conf
    redis-service …/6383/redis.conf
    redis-service …/6384/redis.conf
    redis-service …/6385/redis.conf
  4. 節點加入集羣中,中途需要輸入 yes 確定創建集羣
    redis-trib.rb create --replicas 1 192.168.0.107:6380 192.168.0.107:6381 192.168.0.107:6382 192.168.0.107:6383 192.168.0.107:6384 192.168.0.107:6385
  5. 進入集羣中任何一個節點
    redis-cli -c -h 192.168.0.107 -p 6381
  6. 查看集羣中的節點
    cluster nodes
  7. 增加集羣節點
    cluster meet ip port

     

其他集羣實現方式

一、twemproxy 中間件

  twemproxy 又稱 nutcracker,起源於推特系統中 redis、memcached 集羣的輕量級代理。

  Redis 代理中間件 twemproxy 是一種利用中間件做分片的技術。twemproxy 處於客戶端和服務器的中間,將客戶端發來的請求,進行一定的處理後(sharding),再轉發給後端真正的 redis 服務器。也就是說,客戶端不直接訪問redis服務器,而是通過t wemproxy 代理中間件間接訪問。降低了客戶端直連後端服務器的連接數量,並且支持服務器集羣水平擴展。

  twemproxy 中間件的內部處理是無狀態的,它本身可以很輕鬆地集羣,這樣可以避免單點壓力或故障。  

  twemproxy 是一個單點,很容易對其造成很大的壓力,所以通常會結合 keepalived 來實現 twemproy 的高可用。這時,通常只有一臺 twemproxy 在工作,另外一臺處於備機,當一臺掛掉以後,vip 自動漂移,備機接替工作。關於 keepalived 的用法可自行網上查閱資料。

 

二、codis 中間件
  codis 是一個分佈式的 Redis 解決方案,由豌豆莢開源,對於上層的應用來說,連接 codis proxy 和連接原生的 redis server 沒什麼明顯的區別,上層應用可以像使用單機的 redis 一樣使用,codis底 層會處理請求的轉發,不停機的數據遷移等工作,所有後邊的事情,對於前面的客戶端來說是透明的,可以簡單的認爲後邊連接的是一個內存無限大的 redis 服務。

 

客戶端分片

  分區的邏輯在客戶端實現,由客戶端自己選擇請求到哪個節點。方案可參考一致性哈希,這種方案通常適用於用戶對客戶端的行爲有完全控制能力的場景。

三、Jedis sharding集羣
  Redis Sharding 可以說是在 Redis cluster 出來之前業界普遍的採用方式,其主要思想是採用 hash 算法將存儲數據的 key 進行 hash 散列,這樣特定的 key 會被定爲到特定的節點上。
  慶幸的是,Java Redis 客戶端驅動 Jedis 已支持 Redis Sharding 功能,即 ShardedJedis 以及結合緩存池的 ShardedJedisPool。


Jedis 的 Redis Sharding 實現具有如下特點:

    1. 採用一致性哈希算法,將 key 和節點 name 同時 hashing,然後進行映射匹配,採用的算法是 MURMUR_HASH。採用一致性哈希而不是採用簡單類似哈希求模映射的主要原因是當增加或減少節點時,不會產生由於重新匹配造成的r ehashing。一致性哈希隻影響相鄰節點 key 分配,影響量小。

    2. 爲了避免一致性哈希隻影響相鄰節點造成節點分配壓力,ShardedJedis 會對每個 Redis 節點根據名字(沒有,Jedis 會賦予缺省名字)會虛擬化出 160個 虛擬節點進行散列。根據權重weight,也可虛擬化出 160倍 數的虛擬節點。用虛擬節點做映射匹配,可以在增加或減少 Redis 節點時,key 在各 Redis 節點移動再分配更均勻,而不是隻有相鄰節點受影響。

    3. ShardedJedis 支持 keyTagPattern 模式,即抽取 key 的一部分 keyTag 做 sharding,這樣通過合理命名 key,可以將一組相關聯的 key 放入同一個 Redis 節點,這在避免跨節點訪問相關數據時很重要。

    4. 當然,Redis Sharding這 種輕量靈活方式必然在集羣其它能力方面做出妥協。比如擴容,當想要增加 Redis 節點時,儘管採用一致性哈希,畢竟還是會有 key 匹配不到而丟失,這時需要鍵值遷移。

    5. 作爲輕量級客戶端 sharding,處理 Redis 鍵值遷移是不現實的,這就要求應用層面允許 Redis 中數據丟失或從後端數據庫重新加載數據。但有些時候,擊穿緩存層,直接訪問數據庫層,會對系統訪問造成很大壓力。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章