redis——集群
2019獨角獸企業重金招聘Python工程師標準>>>
現實中redis需要若干臺redis服務器的支持:
(1)從結構上,單個Redis服務器會產生單點故障,同時一臺服務器需要承受所有的請求負載。這就需要為數據生成多個副本并分配在不同的服務器上。
(2)從容量上,單個Redis服務器的內存非常容易成為存儲瓶頸,所以需要進行數據分片。
? 同時擁有多個Redis服務器后就會面臨如何管理集群的問題,包括如何增加節點,故障恢復等操作。
1. 復制
?? ?通過持久化功能,Redis保證了即使在服務器重啟的情況下也不會損失(或少量損失)數據。
?? ?但是由于數據是存儲在一臺服務器上的,如果這臺服務器出現硬盤故障等問題,也會導致數據丟失。為了避免單點故障,通常的做法是將數據庫復制多個副本部署在不同的服務器上,這樣即使有一臺服務器出現故障,其他服務器依然可以繼續提供服務。為此,Redis提供了復制(replication)功能,可以實現當一臺服務器中的數據更新后,自動更新的數據同步到其他數據庫上。
2. 哨兵
?? ?在一個典型的一主多從的Redis系統中,從數據庫在整個系統中起到了數據冗余備份和讀寫分離的作用。當主數據庫遇到異常中斷服務后,開發者可以通過手動的方式選擇一個從數據庫升級為主數據庫,以使得系統能夠繼續提供服務。然而整個過程相對麻煩且需要人工介入,難以實現自動化。為此, Redis2.8中提供了哨兵工具來實現自動的系統監控和故障恢復功能。
3. 集群:
?? ?即使使用哨兵,此時的Redis集群的每個數據庫依然存在集群中的所有的數據庫,從而導致集群的總數據存儲量受限于內存最小的數據庫節點,形成木桶效應。由于Redis中的所有數據都是基于內存存儲,這一問題就尤為突出了,尤其是當使用Redis做持久化存儲服務使用時。
?? 哨兵和集群是兩個獨立的功能,但從特性來看哨兵可以視為集群的子集,當不需要數據分片或者已經在客戶端進行分片的場景下哨兵就足夠使用了,但如果需要進行水平擴容,則集群是一個非常好的選擇。
在Redis中使用復制功能非常容易,只需要在從數據庫的配置文件中加入
salveof 主數據庫地址 主數據庫端口 salveof 127.0.0.1 6379即可,主數據庫無需進行任何配置。
哨兵的作用就是監控Redis系統的運行狀況。它的功能包括以下兩個:
?? * 監控主數據庫和從數據庫是否運行正常。
?? * 主數據庫出現故障時自動將從數據庫轉換為主數據庫。
其中:
?? master-name是一個有大小寫字母,數字和".-_"組成的主數據庫的名字,因為考慮到故障恢復后當前監控系統的主數據庫的地址和端口會產生變化,所以哨兵提供了命令可以通過主數據庫的名字獲取當前系統的主數據庫的地址和端口號。
?? ?ip 表示當前系統中主數據庫的地址,而redis-port則表示端口號。
?? ?quornum 用來表示執行故障恢復操作前至少需要幾個哨兵結點同意。
?? ?
需要注意的是,配置哨兵監控一個系統時,只需要配置其監控主數據庫即可,哨兵會自動發現所有復制該主數據庫的從數據庫。
和主數據庫的連接建立完成后,哨兵會定時執行下面3個操作:
* 每10秒哨兵會向主數據庫和從數據庫發送info命令。
* 每2秒哨兵會向主數據庫和從數據庫的_sentinel_:hello頻道發送自己的信息。
* 每1秒哨兵會向主數據,從數據庫和其他哨兵結點發送PING命令。
領頭哨兵將從停止服務的主數據庫的從數據中挑選一個來充當新的主數據庫。挑選的依據如下:
(1)所有在線的從數據庫中,選擇優先級最高的從數據庫。優先級可以通過slave-priority選項來設置。
(2)如果有多個最高優先級的從數據庫,則復制的命令偏移量越大(即復制越完整)越優先。
(3)如果以上條件都一樣,則選擇運行ID較小的從數據庫。
選出一個從數據庫后,領頭哨兵將向從數據庫發送SLAVEOF NO ONE命令將其升格為主數據庫。而后領頭哨兵向其他從數據庫發送SLAVEOF 命令來使其成為新主數據庫的從數據庫。
最后一步則是更新內部的記錄,將已經停止服務的舊的主數據庫更新為新的主數據庫的從數據庫,使得當其恢復服務時自動以從服務器的身份繼續服務。
哨兵的部署:
?? ?哨兵以獨立進程的方式對一個主從系統進行監控,監控的效果好壞與否取決于哨兵的視角是否有代表性。如果一個主從系統中配置的哨兵較少,哨兵對整個系統的判斷的可靠性就會降低。極端情況下,當只有一個哨兵時,哨兵本身就可能會發生單點故障。整體來講,相對穩妥的哨兵部署方案是使得哨兵的視角盡可能地與每個節點的視角一致,即:
?? ?(1)為每個結點(無論是主數據庫還是從數據庫)部署一個哨兵;
?? ?(2)使得每個哨兵與其對應的節點的網絡環境相同或相近。
?? ?這樣的部署方案可以保證哨兵的視角擁有較高的代表性和可靠性。
?? ?同時設置quorum的值為N/2+1(其中N為哨兵節點數量),這樣使得只有當大部分哨兵節點同意后才會進行故障恢復???
集群:?? ?
?? ????集群的特點在于擁有和單機實例同樣的性能,同時在網絡分區后能夠提供一定的可訪問性以及對主數據庫故障恢復的支持。另外集群支持幾乎所有的單機實例支持的命令,對于涉及多個的命令(如MGET),如果每個鍵都位于同一個節點中,才可以正常支持,否則會提示錯誤。除此之外集群還有一個限制是只能使用默認的0號數據庫,如果執行SELECT 切換數據庫則會提示錯誤。
???? 哨兵和集群是兩個獨立的功能,但從特性來看哨兵可以視為集群的子集,當不需要數據分片或者已經在客戶端進行分片的場景下哨兵就足夠使用了,但如果需要進行水平擴容,則集群是一個非常好的選擇。
配置集群:
?? ?使用集群,只需要將每個數據庫節點的cluster-enabled配置選項打開即可。每個集群中至少需要3個主數據庫才能正常運行。
實驗:
cluster-enabled yes cluster-config-file nodes.7000.conf #個性化 cluster-node-timeout 5000 appendonly yes appendfilename "appendonly.7000.aof" #個性化 daemonize yes pidfile #個性化 log #個性化以7000-70005六個不同的port啟動。
[root@localhost 7005]# ps -aux|grep redis Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ root 4423 0.0 0.1 133540 7552 ? Ssl 14:54 0:00 /mnt/shares/redis/redis-3.2.0/src/redis-server 127.0.0.1:7000 [cluster] root 4432 0.0 0.1 133540 7556 ? Ssl 14:54 0:00 /mnt/shares/redis/redis-3.2.0/src/redis-server 127.0.0.1:7001 [cluster] root 4441 0.0 0.1 133540 7556 ? Ssl 14:55 0:00 /mnt/shares/redis/redis-3.2.0/src/redis-server 127.0.0.1:7002 [cluster] root 4449 0.0 0.1 133540 7556 ? Ssl 14:55 0:00 /mnt/shares/redis/redis-3.2.0/src/redis-server 127.0.0.1:7003 [cluster] root 4454 0.0 0.1 133540 7556 ? Ssl 14:55 0:00 /mnt/shares/redis/redis-3.2.0/src/redis-server 127.0.0.1:7004 [cluster] root 4460 0.0 0.1 133540 7552 ? Ssl 14:56 0:00 /mnt/shares/redis/redis-3.2.0/src/redis-server 127.0.0.1:7005 [cluster] root 4464 0.0 0.0 103252 840 pts/1 S+ 14:56 0:00 grep redis初始化集群
其中create參數表示要初始化集群, --replicas 1?表示每個主數據庫擁有的從數據庫個數為1,所以下面整個集群共有3(6/2)個主數據庫以及3個從數據庫。
[root@localhost src]# ./redis-trib.rb create --replicas 1 172.16.81.107:7000 172.16.81.107:7001 172.16.81.107:7002 172.16.81.107:7003 172.16.81.107:7004 172.16.81.107:7005 >>> Creating cluster >>> Performing hash slots allocation on 6 nodes... Using 3 masters: 172.16.81.107:7000 172.16.81.107:7001 172.16.81.107:7002 Adding replica 172.16.81.107:7003 to 172.16.81.107:7000 Adding replica 172.16.81.107:7004 to 172.16.81.107:7001 Adding replica 172.16.81.107:7005 to 172.16.81.107:7002 M: 9e3a1e4883393552357e41507f3da851370dbe4b 172.16.81.107:7000slots:0-5460 (5461 slots) master M: de7a63cd01e477c49a31f53837b72c7814f4b274 172.16.81.107:7001slots:5461-10922 (5462 slots) master M: c7b1fbf25def810a42f5f61a0b16e62f538861b7 172.16.81.107:7002slots:10923-16383 (5461 slots) master S: d86d4a4f6246362e06f2a266f2a9fec3c5942541 172.16.81.107:7003replicates 9e3a1e4883393552357e41507f3da851370dbe4b S: 43b99ad59f4519f915c86a6ff5bd6189e211e080 172.16.81.107:7004replicates de7a63cd01e477c49a31f53837b72c7814f4b274 S: 7ffe9792caf077cee3f8d545278d08afbf456d77 172.16.81.107:7005replicates c7b1fbf25def810a42f5f61a0b16e62f538861b7 Can I set the above configuration? (type 'yes' to accept): yes >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join >>> Performing Cluster Check (using node 172.16.81.107:7000) M: 9e3a1e4883393552357e41507f3da851370dbe4b 172.16.81.107:7000slots:0-5460 (5461 slots) master M: de7a63cd01e477c49a31f53837b72c7814f4b274 172.16.81.107:7001slots:5461-10922 (5462 slots) master M: c7b1fbf25def810a42f5f61a0b16e62f538861b7 172.16.81.107:7002slots:10923-16383 (5461 slots) master M: d86d4a4f6246362e06f2a266f2a9fec3c5942541 172.16.81.107:7003slots: (0 slots) masterreplicates 9e3a1e4883393552357e41507f3da851370dbe4b M: 43b99ad59f4519f915c86a6ff5bd6189e211e080 172.16.81.107:7004slots: (0 slots) masterreplicates de7a63cd01e477c49a31f53837b72c7814f4b274 M: 7ffe9792caf077cee3f8d545278d08afbf456d77 172.16.81.107:7005slots: (0 slots) masterreplicates c7b1fbf25def810a42f5f61a0b16e62f538861b7 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.集群中所有節點信息:
[root@localhost src]# redis-cli -p 7001 127.0.0.1:7001> cluster nodes de7a63cd01e477c49a31f53837b72c7814f4b274 127.0.0.1:7001 myself,master - 0 0 2 connected 5461-10922 7ffe9792caf077cee3f8d545278d08afbf456d77 127.0.0.1:7005 slave c7b1fbf25def810a42f5f61a0b16e62f538861b7 0 1464161662246 3 connected 9e3a1e4883393552357e41507f3da851370dbe4b 127.0.0.1:7000 master - 0 1464161661243 1 connected 0-5460 c7b1fbf25def810a42f5f61a0b16e62f538861b7 127.0.0.1:7002 master - 0 1464161661744 3 connected 10923-16383 43b99ad59f4519f915c86a6ff5bd6189e211e080 127.0.0.1:7004 slave de7a63cd01e477c49a31f53837b72c7814f4b274 0 1464161660741 2 connected d86d4a4f6246362e06f2a266f2a9fec3c5942541 127.0.0.1:7003 slave 9e3a1e4883393552357e41507f3da851370dbe4b 0 1464161661744 1 connected節點的增加:
CLUSTER MSET ip port?ip ,port 是集群中任意一個節點的地址和端口號,新節點(A)接收到客戶端發來的命令后,
?會與該地址和端口號的節點B進行握手,使B將A認作當前集群中的一員。當B與A握手成功后,B會使用Gossip協議將節點A的信息通知給集群中的每一個節點。通過這一方式,即使集群中有多個節點,也只需要選擇MSET其中任意一個節點,即可使新節點最終加入整個集群中。
?
啟動一個port為7006的節點:
[root@localhost 7006]# /mnt/shares/redis/redis-3.2.0/src/redis-server ./redis.conf
執行下面的命令把新節點添加到集群中:
[root@localhost src]# ./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000 >>> Adding node 127.0.0.1:7006 to cluster 127.0.0.1:7000 >>> Performing Cluster Check (using node 127.0.0.1:7000) M: 9e3a1e4883393552357e41507f3da851370dbe4b 127.0.0.1:7000slots:0-5460 (5461 slots) master1 additional replica(s) S: 7ffe9792caf077cee3f8d545278d08afbf456d77 127.0.0.1:7005slots: (0 slots) slavereplicates c7b1fbf25def810a42f5f61a0b16e62f538861b7 S: 43b99ad59f4519f915c86a6ff5bd6189e211e080 127.0.0.1:7004slots: (0 slots) slavereplicates de7a63cd01e477c49a31f53837b72c7814f4b274 S: d86d4a4f6246362e06f2a266f2a9fec3c5942541 127.0.0.1:7003slots: (0 slots) slavereplicates 9e3a1e4883393552357e41507f3da851370dbe4b M: c7b1fbf25def810a42f5f61a0b16e62f538861b7 127.0.0.1:7002slots:10923-16383 (5461 slots) master1 additional replica(s) M: de7a63cd01e477c49a31f53837b72c7814f4b274 127.0.0.1:7001slots:5461-10922 (5462 slots) master1 additional replica(s) [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. >>> Send CLUSTER MEET to node 127.0.0.1:7006 to make it join the cluster. [OK] New node added correctly. 127.0.0.1:7000> cluster nodes 47a241ed17b89f501d3ea6e337456fab2ddd1e93 127.0.0.1:7006 master - 0 1464163321583 0 connected 7ffe9792caf077cee3f8d545278d08afbf456d77 127.0.0.1:7005 slave c7b1fbf25def810a42f5f61a0b16e62f538861b7 0 1464163320582 6 connected 43b99ad59f4519f915c86a6ff5bd6189e211e080 127.0.0.1:7004 slave de7a63cd01e477c49a31f53837b72c7814f4b274 0 1464163320082 5 connected d86d4a4f6246362e06f2a266f2a9fec3c5942541 127.0.0.1:7003 slave 9e3a1e4883393552357e41507f3da851370dbe4b 0 1464163320082 4 connected c7b1fbf25def810a42f5f61a0b16e62f538861b7 127.0.0.1:7002 master - 0 1464163322084 3 connected 10923-16383 9e3a1e4883393552357e41507f3da851370dbe4b 127.0.0.1:7000 myself,master - 0 0 1 connected 0-5460 de7a63cd01e477c49a31f53837b72c7814f4b274 127.0.0.1:7001 master - 0 1464163321083 2 connected 5461-10922插槽的分配:
??? 未完成
獲取與插槽對應的結點:
?? ?當客戶端向集群中的任意一個結點發送命令后,該結點會判斷相應的鍵是否在當前結點中,
?? ?如果鍵在該結點中,則會像單機實例一樣正常處理該命令;
?? ?如果鍵不在該結點中,就會返回一個MOVE重定向請求,告訴客戶端這個鍵目前由哪個結點負責,然后客戶端再將同樣的請求向目標節點重新發送一次以獲取結果。
?? ?
?? ?Redis命令行客戶端提供了集群模式來支持自動重定向,使用-c參數來啟用:
加入參數-c后,如果當前結點并不負責要處理的鍵,Redis命令行客戶端會進行自動命令重定向。而這一過程正是每個支持集群的客戶端應該實現的。
?? ?
故障恢復:
?? ?在集群中,當一個主數據庫下線時,就會出現一部分插槽無法寫入的問題。這時如果該主數據庫擁有至少一個從數據庫,集群就進行故障恢復操作來將其中一個從數據庫轉變成主數據庫來保證 集群的完整。選擇哪個從數據庫來作為主數據庫的過程與在哨兵中選擇領頭哨兵的過程一樣,都是 Raft算法,過程如下:
?? ?1. 發現復制的主數據庫下線的從數據庫(下面稱作A)向每個集群中的節點發送請求,要求對方選自己成為主數據庫。
?? ?2. 如果收到請求的節點沒有選過其他人,則會同意將A設置為主數據庫。
?? ?3. 如果A發現有超過集群中節點總數一半的節點同意選自己成為主數據庫,則A則成功成為主數據庫。
?? ?4. 當有多個從數據庫結點同時參選主數據庫,則會出現沒有任何節點當選的可能。此時每個參選結點節點將等待一個隨機時間重新發起參選請求,進行下一輪選舉,直到選舉成功。
?? ?? ?
?? ??? 當某個從數據庫當選為主數據庫后,會通過命令SLAVEOF ON ONE 將自己轉換成主數據庫,并將舊的主數據庫的插槽轉換給自己負責。
?? ? ?
?? ?? 如果一個至少負責一個插槽的主數據庫下線且沒有相應的從數據庫可以進行故障恢復,則整個集群默認會進入下線狀態無法繼續工作。如果想在這種情況下使集群仍能正常工作,可以
?? ?? 修改配置cluster-require-full-coverage 為no(默認yes)???
?
轉載于:https://my.oschina.net/lvhuizhenblog/blog/680287
總結
- 上一篇: 笔记整理之 SHELL 变量
- 下一篇: 【数据结构与算法】【算法思想】位图