詳細剖析 Redis 三種集群策略

redis

redis是單線程的,但是一般的作為緩存使用的話,redis足夠了,因為它的讀寫速度太快了。
官方的一個簡單測試:
測試完成了50個并發執行100000個請求。
設置和獲取的值是一個256字節字符串。
結果:讀的速度是110000次/s,寫的速度是81000次/s
但對于訪問量特別大的服務來說,還是稍有不足。那么,如何提升redis的性能呢?搭建集群。

redis主要提供三種集群策略:

  1. 主從復制
  2. 集群
  3. 哨兵

一、主從復制

在主從復制中,數據庫分為倆類,主數據庫(master)和從數據庫(slave)。

1.1 主從復制有如下特點:

  1. 主數據庫可以進行讀寫操作,當讀寫操作導致數據變化時會自動將數據同步給從數據庫
  2. 從數據庫一般都是只讀的,并且接收主數據庫同步過來的數據
  3. 一個master可以擁有多個slave,但是一個slave只能對應一個master

1.2 工作機制

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

  2. 復制初始化后,master每次接收到的寫命令都會同步發送給slave,保證主從數據一致性。

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

1.3 主從配置

redis默認是主數據,所以master無需配置,我們只需要修改slave的配置即可。
設置需要連接的master的ip端口:
slaveof 192.168.0.107 6379
如果master設置了密碼。需要配置:
masterauth master-password
連接成功進入命令行后,可以通過以下命令行查看連接該數據庫的其他庫信息:
info replication

1.3 優點

  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.4 缺點

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

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

  3. Redis的主從復制采用全量復制,復制過程中主機會fork出一個子進程對內存做一份快照,并將子進程的內存快照保存為文件發送給從機,這一過程需要確保主機有足夠多的空余內存。若快照文件較大,對集群的服務能力會產生較大的影響,而且復制過程是在從機新加入集群或者從機和主機網絡斷開重連時都會進行,也就是網絡波動都會造成主機和從機間的一次全量的數據復制,這對實際的系統運營造成了不小的麻煩。

  4. Redis較難支持在線擴容,在集群容量達到上限時在線擴容會變得很復雜。為避免這一問題,運維人員在系統上線時必須確保有足夠的空間,這對資源造成了很大的浪費。

  5. 其實redis的主從模式很簡單,在實際的生產環境中是很少使用的,我也不建議在實際的生產環境中使用主從模式來提供系統的高可用性,之所以不建議使用都是由它的缺點造成的,在數據量非常大的情況,或者對系統的高可用性要求很高的情況下,主從模式也是不穩定的。

二、哨兵

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

2.1 哨兵的作用是監控 redis系統的運行狀況,功能如下

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

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

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

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

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

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

2.2 哨兵工作機制

  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命令,哨兵可以獲取主從數據庫的最新信息,并進行相應的操作,比如角色變更等。

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

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

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

  4. 如上文所說,哨兵認為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

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

2.3 哨兵配置

哨兵配置的配置文件為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

### Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=192.168.0.107:6379,slaves=2,sentinels=1

三、集群

3.1 特點

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節點,整個集群繼續對外提供服務。

3.2 配置

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

[root@buke107 src]### redis-cli -c -h 192.168.0.107 -p 6381
192.168.0.107:6381> cluster nodes
868456121fa4e6c8e7abe235a88b51d354a944b5 192.168.0.107:6382 master - 0 1523609792598 3 connected 10923-16383
d6d01fd8f1e5b9f8fc0c748e08248a358da3638d 192.168.0.107:6385 slave 868456121fa4e6c8e7abe235a88b51d354a944b5 0 1523609795616 6 connected
5cd3ed3a84ead41a765abd3781b98950d452c958 192.168.0.107:6380 master - 0 1523609794610 1 connected 0-5460
b8e047aeacb9398c3f58f96d0602efbbea2078e2 192.168.0.107:6383 slave 5cd3ed3a84ead41a765abd3781b98950d452c958 0 1523609797629 1 connected
68cf66359318b26df16ebf95ba0c00d9f6b2c63e 192.168.0.107:6384 slave 90b4b326d579f9b5e181e3df95578bceba29b204 0 1523609796622 5 connected
90b4b326d579f9b5e181e3df95578bceba29b204 192.168.0.107:6381 myself,master - 0 0 2 connected 5461-10922

如上所示,三主三從節點已創建成功

7.增加集群節點
cluster meet ip port


其他集群實現方式

中間件

一、twemproxy

  1. twemproxy又稱nutcracker,起源于推特系統中redis、memcached集群的輕量級代理。

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

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

  4. 從下面架構圖看到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。采用一致性哈希而不是采用簡單類似哈希求模映射的主要原因是當增加或減少節點時,不會產生由于重新匹配造成的rehashing。一致性哈希只影響相鄰節點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中數據丟失或從后端數據庫重新加載數據。但有些時候,擊穿緩存層,直接訪問數據庫層,會對系統訪問造成很大壓力。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內容