目錄
基本概念
數據分片算法
哈希求余
?編輯一致性哈希算法
哈希槽分區算法
搭建集群環境
創建目錄和配置
編寫 docker-compose.yml
啟動容器
構建集群
基本概念
廣義的集群,只要是多個機器構成了分布式系統,都可以成為是一個“集群”。
但是redis提供的集群模式是狹義的集群,這個集群模式之下,主要是解決,存儲空間不足的問題(拓展存儲空間)。
哨兵模式提高了系統的可用性。哨兵模式中本質上還是redis主從節點存儲數據。其中就要求一個主節點/從節點,就得存儲整個數據的“全集”。
此處關鍵問題,就是引入多臺機器,每臺機器存儲一部分數據。每個存儲數據的機器還需要搭配若干個從節點。
每個 Slave 都是對應 Master 的備份(當 Master 掛了, 對應的 Slave 會補位成 Master).
每個紅框部分都可以稱為是?個 分片(Sharding).
把數據分成多份,具體該如何去分呢?
數據分片算法
有三中主流的分片方式。
哈希求余
借鑒了哈希表的基本思想。借助hash函數,把一個key映射到一個數組下標上。再針對數組的長度求余,就可以得到一個數組下標。
比如有三個分片,編號0,1,2。此時就可以針對要插入的數據的key(redis都是鍵值對結構的數據)計算hash值。(比如md5)再把這個hash值余上分片個數,就得到了一個下標。此時就可以把這個數據放到該下標對應的分片中了。
后續查詢key的時候,也是同樣的算法。
拓展:MD5是一個非常廣泛使用的hash算法。
????????特點:1.md5計算結果是定長的。無論輸入的原字符串多長,最終算出的結果就是固定長度。
? ? ? ? ? ? ? ? ? ?2.md5計算結果是分散的。兩個原字符串,哪怕大部分都相同,只有一個小的地方不同,算出來的md5值也會差別很大。
? ? ? ? ? ? ? ? ? ?3.md5計算結果是不可逆的。給你原字符串,可以很容易算出md5的值。給你md5值,很難還原出原始的字符串的。
一旦服務器集群需要擴容,就需要更高的成本。分片主要目的就是為了能提高存儲能力。分片越多,能存的數據越多,成本也更高。隨著業務的增長,數據變多了,需要更多的分片。
當引入更多的分片的時候,N(分片數量)就改變了。如果發現某個數據,在擴容之后,不應該在當前的分片中了,就需要重新進行分配(搬運數據)。絕大部分數據都需要進行搬運,成本過高。
一致性哈希算法
?1)把 0 -> 2^32-1 這個數據空間, 映射到?個圓環上. 數據按照順時針?向增?。
2)假設當前存在三個分?, 就把分?放到圓環的某個位置上。
3)?假定有?個 key, 計算得到 hash 值 H, 那么這個 key 映射到哪個分?呢? 規則很簡單, 就是從 H
所在位置,順時針往下找, 找到的第?個分?, 即為該 key 所從屬的分?。
這就相當于, N 個分?的位置, 把整個圓環分成了 N 個管轄區間。Key 的 hash 值落在某個區間內, 就歸對應區間管理。
此時,如果增加分片,會怎樣呢?
原有分?在環上的位置不動, 只要在環上新安排?個分片位置即可。
此時, 只需要把 0 號分片上的部分數據,搬運給 3 號分片即可。1 號分片和 2 號分片管理的區間都是不變的。
優點: 大大降低了擴容時數據搬運的規模, 提?了擴容操作的效率。
缺點: 數據分配不均勻 (有的多有的少, 數據傾斜)。
哈希槽分區算法
這是redis真正采用的分片算法。采用以下公式:
hash_slot = crc16(key) % 16384
crc16也是一種計算hash的算法,這里不多展開。
16384 = 16* 1024
得到結果hash_slot,稱為哈希槽。
會把這些哈希槽分配到不同的分片上。
此時,這三個分片上的數據就是比較均勻的了。會先根據key算出哈希槽,再根據哈希槽,映射到分片。這種算法,本質就是把上述兩種方式結合了一下。
這里只是一種可能的方式,實際上分片是非常靈活的。
每個分片持有的槽位號,可以是連續的,也可以是不連續的。
此時,每個分片都會使用“位圖”這樣的數據結構,表示出當前有多少槽位號。
16384個bit位,用每一位0 / 1來區分自己這個分片當前是否持有該槽位號。??
進行擴容:
上述過程中,只有被移動的槽位,對應的數據才需要搬運。降低了搬運成本。
redis中,當前某個分片包含哪些槽位,都是可以手動配置的。
問題1:Redis集群最多有16384個分片嗎?
如果?個分片只有?個槽位, 這對于集群的數據均勻其實是難以保證的。
實際上 Redis 的作者建議集群分片數不應該超過 1000。而且, 16000 這么?規模的集群, 本?的可用性也是?個?問題. ?個系統越復雜, 出現故障的概率是越?的。
問題2:為什么是16384個槽位?
節點之間通過心跳包通信,心跳包中包含該節點持有那些槽位。如果16384個槽位,每個分片需要2KB大小的位圖,來表示持有的槽位。如果槽位更多,就需要消耗更大的空間,更大的網絡帶寬。16384是比較均衡的值,個數上基本夠用,也不會占據大的硬件資源(網絡帶寬)。
搭建集群環境
這里使用docker來搭建redis集群。
創建目錄和配置
創建 redis-cluster ?錄。內部創建兩個?件。
redis-cluster/
├── docker-compose.yml
└── generate.sh
generate.sh 內容如下:
for port in $(seq 1 9); \
do \
mkdir -p redis${port}/
touch redis${port}/redis.conf
cat << EOF > redis${port}/redis.conf
port 6379
bind 0.0.0.0
protected-mode no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.30.0.10${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
EOF
done
# 注意 cluster-announce-ip 的值有變化.
for port in $(seq 10 11); \
do \
mkdir -p redis${port}/
touch redis${port}/redis.conf
cat << EOF > redis${port}/redis.conf
port 6379
bind 0.0.0.0
protected-mode no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.30.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
EOF
done
在linux上,以.sh后綴結尾的文件,稱為“shell腳本”。使用linux的時候,都是通過一些命令來進行操作的。使用命令操作,就非常適合把命令給寫到一個文件中,批量化執行。同時還能加入條件,循環,函數等機制。就可以基于這些來完成更復雜的工作了。
此時需要創建11個redis節點,這些redis的配置文件內容,大同小異。此時就可以使用腳本來批量生成。
? cluster-enabled yes 開啟集群。
? cluster-config-file nodes.conf 集群節點?成的配置。
? cluster-node-timeout 5000 節點失聯的超時時間。
? cluster-announce-ip 172.30.0.101 節點?? ip。
? cluster-announce-port 6379 節點??的業務端?。
? cluster-announce-bus-port 16379 節點??的總線端?. 集群管理的信息交互是通過這個端口進行的。
編寫 docker-compose.yml
? 先創建 networks, 并分配?段為 172.30.0.0/24
? 配置每個節點. 注意配置?件映射, 端?映射, 以及容器的 ip 地址. 設定成固定 ip ?便后續的觀察和操作。
version: '3.7'
networks:
mynet:
ipam:
config:
- subnet: 172.30.0.0/24
services:
redis1:
image: 'redis:5.0.9'
container_name: redis1
restart: always
volumes:
- ./redis1/:/etc/redis/
ports:
- 6371:6379
- 16371:16379
command:
redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.101
redis2:
image: 'redis:5.0.9'
container_name: redis2
restart: always
volumes:
- ./redis2/:/etc/redis/
ports:
- 6372:6379
- 16372:16379
command:
redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.102
redis3:
image: 'redis:5.0.9'
container_name: redis3
restart: always
volumes:
- ./redis3/:/etc/redis/
ports:
- 6373:6379
- 16373:16379
command:
redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.103
啟動容器
docker-compose up -d
構建集群
接下來,要把上述redis節點構建成集群。
redis-cli --cluster create 172.30.0.101:6379 172.30.0.102:6379
172.30.0.103:6379 172.30.0.104:6379 172.30.0.105:6379 172.30.0.106:6379
172.30.0.107:6379 172.30.0.108:6379 172.30.0.109:6379 --cluster-replicas 2
列出每個參與構建集群的ip和端口。(端口都是容器內部的端口)
cluster create: 表?建?集群. 后?填寫每個節點的 ip 和地址。
cluster-replicas 2: 描述集群的每個主節點,應該有2個從節點。一共9個節點,設置了之后,redis就知道了3個節點是一伙的(一個分片)。
執?之后, 容器之間會進?加?集群操作。?志中會描述哪些是主節點, 哪些從節點跟隨哪個主節點。
>>> Performing hash slots allocation on 9 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.30.0.105:6379 to 172.30.0.101:6379
Adding replica 172.30.0.106:6379 to 172.30.0.101:6379
Adding replica 172.30.0.107:6379 to 172.30.0.102:6379
Adding replica 172.30.0.108:6379 to 172.30.0.102:6379
Adding replica 172.30.0.109:6379 to 172.30.0.103:6379
Adding replica 172.30.0.104:6379 to 172.30.0.103:6379
以上,關于redis集群,希望對你有所幫助。