Redis集群搭建的三種方式


一、Redis主從

1.1 Redis主從原理

和MySQL需要主從復制的原因一樣,Redis雖然讀取寫入的速度都特別快,但是也會產生性能瓶頸,特別是在讀壓力上,為了分擔壓力,Redis支持主從復制。Redis的主從結構一主一從,一主多從或級聯結構,復制類型可以根據是否是全量而分為全量同步和增量同步。
下圖為級聯結構:
image.png

1.1.1 全量同步

Redis全量復制一般發生在slave的初始階段,這時slave需要將master上的數據都復制一份,具體步驟如下:
(1)、slave連接master,發送SYNC命令;
(2)、master街道SYNC命令后,執行BGSAVE命令生產RDB文件並使用緩沖區記錄此后執行的所有寫命令;
(3)、master的BGSAVE執行完成后,向所有的slave發送快照文件,並在發送過程中繼續記錄執行的寫命令;
(4)、slave收到快照后,丟棄所有的舊數據,載入收到的數據;
(5)、master快照發送完成后就會開始向slave發送緩沖區的寫命令;
(6)、slave完成對快照的載入,並開始接受命令請求,執行來自master緩沖區的寫命令;
(7)、slave完成上面的數據初始化后就可以開始接受用戶的讀請求了。
大致流程圖如下:
image.png

1.1.2 增量復制

增量復制實際上就是在slave初始化完成后開始正常工作時master發生寫操作同步到slave的過程。增量復制的過程主要是master每執行一個寫命令就會向slave發送相同的寫命令,slave接受並執行寫命令,從而保持主從一致。

1.2 Redis主從同步的策略

主從同步剛連接的時候進行全量同步;全量同步結束后開始增量同步。如果有需要,slave在任何時候都可以發起全量同步,其主要策略就是無論如何首先會嘗試進行增量同步,如果步成功,則會要求slave進行全量同步,之后再進行增量同步。
注意:如果多個slave同時斷線需要重啟的時候,因為只要slave啟動,就會和master建立連接發送SYNC請求和主機全量同步,如果多個同時發送SYNC請求,可能導致master IO突增而發送宕機。

1.3 Redis主從同步的特點

(1)、采用異步復制;
(2)、可以一主多從;
(3)、主從復制對於master來說是非阻塞的,也就是說slave在進行主從復制的過程中,master依然可以處理請求;
(4)、主從復制對於slave來說也是非阻塞的,也就是說slave在進行主從復制的過程中也可以接受外界的查詢請求,只不過這時候返回的數據不一定是正確的。為了避免這種情況發生,可以在slave的配置文件中配置,在同步過程中阻止查詢;
(5)、每個slave可以接受來自其他slave的連接;
(6)、主從復制提高了Redis服務的擴展性,避免單節點問題,另外也為數據備份冗余提供了一種解決方案;
(7)、為了降低主redis服務器寫磁盤壓力帶來的開銷,可以配置讓主redis不在將數據持久化到磁盤,而是通過連接讓一個配置的從redis服務器及時的將相關數據持久化到磁盤,不過這樣會存在一個問題,就是主redis服務器一旦重啟,因為主redis服務器數據為空,這時候通過主從同步可能導致從redis服務器上的數據也被清空;

1.4 Redis主從同步的搭建

下載軟件包
# wget http://download.redis.io/releases/redis-4.0.10.tar.gz

解壓軟件包,主從都需要做(本次是單機多實例來做主從)
# tar xf redis-4.0.10.tar.gz -C /home/redis/6379/
# tar xf redis-4.0.10.tar.gz -C /home/redis/6380/

編譯安裝
# yum install -y gcc gcc++
# make MALLOC=libc

啟動Redis(修改了Redis運行方式為守護進程方式)
# /home/redis/6379/redis-4.0.10/src/redis-server /home/redis/6379/redis-4.0.10/redis.conf

從庫做相同的操作,由於是單機多實例,所以修改從庫的port為6380,啟動從庫
# /home/redis/6380/redis-4.0.10/src/redis-server /home/redis/6380/redis-4.0.10/redis.conf

Redis測試
master:
[root@alex redis-4.0.10]# ./src/redis-cli
127.0.0.1:6379> set name 111
OK
127.0.0.1:6379> get name
"111"
slave:
[root@alex redis-4.0.10]# ./src/redis-cli -p 6380
127.0.0.1:6380> set name slave
OK
127.0.0.1:6380> get name
"slave"

主從搭建
1、只需要配置slave,指明master的IP地址和port就可以了,配置完后重啟實例
# slaveof <masterip> <masterport>
slaveof 127.0.0.1 6379

測試:
master:
[root@alex redis-4.0.10]# ./src/redis-cli
127.0.0.1:6379> set password 123456
OK
127.0.0.1:6379> get password
"123456"

slave:
[root@alex redis-4.0.10]# ./src/redis-cli -p 6380
127.0.0.1:6380> get password
"123456"

在master上設定鍵值對,在slave上能夠准確的查出來,主從配置成功

二、Redis哨兵

2.1 Redis哨兵機制

在主從復制實現之后,如果想對master進行監控,Redis提供了一種哨兵機制,哨兵的含義就是監控Redis系統的運行狀態,並做相應的響應。

2.1.1 哨兵的功能

其主要的功能有以下兩點:
(1)、監控所有Redis節點是否正常運行;
(2)、master故障后可以通過投票機制,從slave中選舉出新的master,保證集群正常運行。
在一個一主多從的集群中,可以啟用多個哨兵進行監控以保證集群足夠穩健,這種情況下,哨兵不僅監控主從服務,哨兵之間也會相互監控,建議哨兵至少3個並且是奇數。

2.1.2 哨兵的任務

哨兵主要用於管理多個Redis服務器,主要有以下三個任務:
(1)、監控:哨兵會不斷的檢測master和slave之間是否運行正常;
(2)、提醒:當監控的某個Redis出現問題,哨兵可以通過API向管理員或其他應用程序發送通知;
(3)、故障遷移:當一個master不能正常工作時,哨兵會開始一次自動故障遷移操作,它會將失效master的其中一個slave提升為master,並讓失效master和其他slave該為復制新的master,當客戶端試圖連接失效的master時,集群也會向客戶端返回新的master地址,使得集群可以使用新的master代替失效的master。

2.2 Redis哨兵的工作原理

哨兵是一個分布式系統,你可以在一個架構中運行多個哨兵(sentinel) 進程,這些進程使用流言協議來接收關於Master是否下線的信息,並使用投票協議來決定是否執行自動故障遷移,以及選擇哪個Slave作為新的Master。
每個哨兵會向其它哨兵、master、slave定時發送消息,以確認對方是否”活”着,如果發現對方在指定時間(可配置)內未回應,則暫時認為對方已掛。若“哨兵群”中的多數sentinel都報告某一master沒響應,系統才認為該master"徹底死亡",通過一定的vote算法,從剩下的slave節點中,選一台提升為master,然后自動修改相關配置。
雖然哨兵釋出為一個單獨的可執行文件 redis-sentinel ,但實際上它只是一個運行在特殊模式下的 Redis 服務器,你可以在啟動一個普通 Redis 服務器時通過給定 --sentinel 選項來啟動哨兵。

2.2.1 監控

sentinel會每秒一次的頻率與之前創建了命令連接的實例發送PING,包括主服務器、從服務器和sentinel實例,以此來判斷當前實例的狀態。down-after-milliseconds時間內PING連接無效,則將該實例視為主觀下線。之后該sentinel會向其他監控同一主服務器的sentinel實例詢問是否也將該服務器視為主觀下線狀態,當超過某quorum后將其視為客觀下線狀態。
當一個主服務器被某sentinel視為客觀下線狀態后,該sentinel會與其他sentinel協商選出零頭sentinel進行故障轉移工作。每個發現主服務器進入客觀下線的sentinel都可以要求其他sentinel選自己為領頭sentinel,選舉是先到先得。同時每個sentinel每次選舉都會自增配置紀元,每個紀元中只會選擇一個領頭sentinel。如果所有超過一半的sentinel選舉某sentinel領頭sentinel。之后該sentinel進行故障轉移操作。
如果一個Sentinel為了指定的主服務器故障轉移而投票給另一個Sentinel,將會等待一段時間后試圖再次故障轉移這台主服務器。如果該次失敗另一個將嘗試,Redis Sentinel保證第一個活性(liveness)屬性,如果大多數Sentinel能夠對話,如果主服務器下線,最后只會有一個被授權來故障轉移。 同時Redis Sentinel也保證安全(safety)屬性,每個Sentinel將會使用不同的配置紀元來故障轉移同一台主服務器。

2.2.2 故障遷移

首先是從主服務器的從服務器中選出一個從服務器作為新的主服務器。選點的依據依次是:網絡連接正常->5秒內回復過INFO命令->10*down-after-milliseconds內與主連接過的->從服務器優先級->復制偏移量->運行id較小的。選出之后通過slaveif no ont將該從服務器升為新主服務器。
其次通過slaveof ip port命令讓其他從服務器復制該信主服務器。
最后當舊主重新連接后將其變為新主的從服務器。注意如果客戶端與就主服務器分隔在一起,寫入的數據在恢復后由於舊主會復制新主的數據會造成數據丟失。
故障轉移成功后會通過發布訂閱連接廣播新的配置信息,其他sentinel收到后依據配置紀元更大來更新主服務器信息。Sentinel保證第二個活性屬性:一個可以相互通信的Sentinel集合會統一到一個擁有更高版本號的相同配置上。    

2.2.3 缺點

(1)、主從服務器的數據要經常進行主從復制,這樣會造成性能下降;
(2)、當主服務器宕機后,從服務器切換成主服務器的那段時間,服務是不可用的。

2.3 Redis哨兵模式搭建

主從模式搭建看上面的步驟,以下主要是在主從搭建完的基礎上搭建哨兵模式。

配置sentinel.conf文件
#工作路徑,注意路徑不要和主重復
dir "/tmp/23679"
#哨兵監控的master,主從配置一樣,這里只用輸入redis主節點的ip/port和法定人數。
sentinel monitor mymaster 192.168.125.128 6379 1
# master或slave多長時間(默認30秒)不能使用后標記為s_down狀態。
sentinel down-after-milliseconds mymaster 5000
#若sentinel在該配置值內未能完成failover操作(即故障時master/slave自動切換),則認為本次failover失敗。
sentinel failover-timeout mymaster 18000
#指定了在執行故障轉移時, 最多可以有多少個從服務器同時對新的主服務器進行同步,有幾個slave就設置幾個
sentinel parallel-syncs mymaster 2


# 啟動哨兵
/home/redis/26379/redis-4.0.10/src/redis-server /home/redis/26379/redis-4.0.10/sentinel.conf --sentinel

通過哨兵查看集群狀態
[root@alex redis-4.0.10]# ./src/redis-cli -p 26379
127.0.0.1:26379> sentinel master mymaster
 1) "name"
2) "mymaster"
3) "ip"
4) "172.16.0.169"
5) "port"
6) "6379"
7) "runid"

127.0.0.1:26379> sentinel slaves mymaster
1)  1) "name"                                           # slave 1
    2) "127.0.0.1:6381"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "6381"
    7) "runid"
    8) "0419f313098f6af1b4ccdb189d6beb22edf27a1c"

2)  1) "name"                                            # slave2
    2) "127.0.0.1:6380"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "6380"
    7) "runid"
    8) "5b00b502a93245f7916efd1f564bd40b16aa7b22"

模擬主down掉
觀察sentinel的狀態
127.0.0.1:26379> sentinel master mymaster
1) "name"
2) "mymaster"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "6381"                # 已經從6379切換到6381
7) "runid"
8) "0419f313098f6af1b4ccdb189d6beb22edf27a1c"

# 相應的slave也做了切換
127.0.0.1:26379> sentinel slaves mymaster
1)  1) "name"
    2) "127.0.0.1:6379"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "6379"
    7) "runid"
    8) ""
    9) "flags"
   10) "s_down,slave,disconnected"

2)  1) "name"
    2) "127.0.0.1:6380"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "6380"
    7) "runid"
    8) "5b00b502a93245f7916efd1f564bd40b16aa7b22"

三、Redis集群

Redis在3.0版本開始正式引用集群特性,Redis集群是一個分布式,高容錯的內存K/V系統,集群可以使用的功能是普通單機Redis所使用的功能的一個子集,比如,Redis集群並不支持處理多個keys的命令,因為這需要在不同節點間移動數據,從而達不到像Redis那樣的性能,在高負載的情況下可能會出現無法預估的錯誤。

3.1 Redis集群的特征

Redis集群有以下幾個重要的特征:
(1)、Redis集群的分片特征在於將空間拆分為16384個槽位,某一個節點負責其中一些槽位;
(2)、Redis集群提供一定程度的可用性,可以在某個節點宕機或者不可達的情況繼續處理命令;
(3)、Redis集群不存在中心節點或代理節點,集群的其中一個最重要的設計目標是達到線性可擴展性;
其架構如下:
image.png

其中每一個圓代表一個節點,任何兩個節點是互通的,可以歸納以下幾點:

  • 所有的節點相互連接;
  • 集群消息通信通過集群總線通信,,集群總線端口大小為客戶端服務端口+10000,這個10000是固定值;
  • 節點與節點之間通過二進制協議進行通信;
  • 客戶端和集群節點之間通信和通常一樣,通過文本協議進行;
  • 集群節點不會代理查詢;

3.2 Redis集群的原理

Redis Cluster中有一個16384長度的槽的概念,他們的編號為0、1、2、3……16382、16383。這個槽是一個虛擬的槽,並不是真正存在的。正常工作的時候,Redis Cluster中的每個Master節點都會負責一部分的槽,當有某個key被映射到某個Master負責的槽,那么這個Master負責為這個key提供服務,至於哪個Master節點負責哪個槽,這是可以由用戶指定的,也可以在初始化的時候自動生成(redis-trib.rb腳本)。這里值得一提的是,在Redis Cluster中,只有Master才擁有槽的所有權,如果是某個Master的slave,這個slave只負責槽的使用,但是沒有所有權。
如下所示:
image.png
那么Redis集群是怎么存儲的呢?
首先,在redis的每一個節點上,都有這么兩個東西,一個是插槽(slot)可以理解為是一個可以存儲兩個數值的一個變量這個變量的取值范圍是:0-16383。還有一個就是cluster我個人把這個cluster理解為是一個集群管理的插件。當我們的存取的key到達的時候,redis會根據crc16的算法得出一個結果,然后把結果對 16384 求余數,這樣每個 key 都會對應一個編號在 0-16383 之間的哈希槽,通過這個值,去找到對應的插槽所對應的節點,然后直接自動跳轉到這個對應的節點上進行存取操作。
還有就是因為如果集群的話,是有好多個redis一起工作的,那么,就需要這個集群不是那么容易掛掉,所以,理論上就應該給集群中的每個節點至少一個備用的redis服務,這個備用的redis稱為從節點(slave)。然后,每一個節點都存有這個集群所有主節點以及從節點的信息,它們之間通過互相的ping-pong判斷是否節點可以連接上。
如果有一半以上的節點去ping一個節點的時候沒有回應,集群就認為這個節點宕機了,然后去連接它的備用節點。如果某個節點和所有從節點全部掛掉,我們集群就進入faill狀態。還有就是如果有一半以上的主節點宕機,那么我們集群同樣進入發力了狀態。這就是我們的redis的投票機制。
具體的原理圖如下:
image.png

(1)、投票過程是集群中所有master參與,如果半數以上master節點與master節點通信超時(cluster-node-timeout)認為當前master節點掛掉.
(2)、什么時候整個集群不可用(cluster_state:fail)
    a、如果集群任意master掛掉,且當前master沒有slave.集群進入fail狀態,也可以理解成集群的slot映射[0-16383]不完整時進入fail狀態. ps : redis-3.0.0.rc1加入cluster-require-full-coverage參數,默認關閉,打開集群兼容部分失敗。
    b、如果集群超過半數以上master掛掉,無論是否有slave,集群進入fail狀態。

3.3 Redis集群搭建

要求:至少6個節點,3主3從。

Redis.conf配置:大致如下
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

# 創建集群
    redis集群的命令工具redis-trib可以讓我們創建集群變得非常簡單。redis-trib是一個用ruby寫的腳本,用於給各節點發指令創建集群、檢查集群狀態或給集群重新分片等。
    redis-trib在Redis源碼的src目錄下,需要gem redis來運行redis-trib。

# 安裝ruby環境
# yum install rubygems -y
1.安裝curl
sudo yum install curl
2. 安裝RVM
curl -L get.rvm.io | bash -s stable 
3. 
source /usr/local/rvm/scripts/rvm
4. 查看rvm庫中已知的ruby版本
rvm list known
5. 安裝一個ruby版本
rvm install 2.3.3
6. 使用一個ruby版本
rvm use 2.3.3
7. 設置默認版本
rvm remove 2.0.0
8. 卸載一個已知版本
ruby --version
9. 再安裝redis就可以了
gem install redis


# 啟動集群
./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005


# 測試集群
[root@alex redis_cluster]# 7000/redis-4.0.10/src/redis-cli -c -p 7000
127.0.0.1:7000> set name alex
-> Redirected to slot [5798] located at 127.0.0.1:7001
OK
127.0.0.1:7001> get name
"alex"

3.4 Redis集群遷移

用戶需要把redis的數據遷移到redis集群,原來的數據可能是只有一個主節點,也可能是用已有的方式分片過,key被存儲在N個幾節點中。
上面2中情況都很容易遷移,特別重要的細節是是否使用了多個key以及是如何使用多個key的。下面是3種不同的情況:

  1. 沒有操作多個key(包括操作多個key的指令、事務、lua腳本)。所有key都是獨立操作的.
  2. 操作了多個key(包括操作多個key的指令、事務、lua腳本),但這些key都有相同的哈希標簽,比如這些被同時操作的key:SUNION{user:1000}.foo {user:1000}.bar
  3. 操作了多個key(包括操作多個key的指令、事務、lua腳本),這些key沒有特別處理,也沒有相同標簽。

第三種情況redis集群沒法處理,需要修改應用程序,不要使用多個key,或者給這些key加上相同的哈希標簽。

第一和第二種情況可以處理,而且他們的處理方式一樣。

    假設你已有的數據被分成N個主節點存儲(當N=1時,就是沒有分片的情況),要把數據遷移到redis集群,需要執行下面幾個步驟:

  1. 停止你的客戶端。目前沒有自動在線遷移到redis集群的方法。你可以自己策划如何讓你的應用程序支持在線遷移。
  2. 使用BGREWRITEAOF指令讓所有主節點產生AOF文件,並且等待這些文件創建完成。
  3. 把這些AOF文件保存下來,分別命名為aof-1, aof-2, ..aof-N,如果需要,可以停止原來的redis實例(對於非虛擬化部署,需要重用這台電腦來說,把舊進程停掉很有幫助)。
  4. 創建N個主節點+0個從節點的redis集群。晚些時候再添加從節點。請確認所有節點都開啟了appendonly的配置。
  5. 停止集群的所有節點,然后用剛才保存的AOF文件,代替每個節點的AOF文件,aof-1給第一個節點,aof-2給第二個節點,以此類推。
  6. 重啟所有節點,這些節點可能會提示說根據配置有些key不應該存儲在這個節點。
  7. 使用redis-trib fix指令,讓集群自動根據哈希槽遷移數據
  8. 使用redis-trib check指令確保你的集群是正常的
  9. 讓你的客戶端使用redis集群客戶端庫,並重啟它。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM