Redis7之集群(十)
10.1 介紹
-
由于數據量過大,單個Master復制集難以承擔,因此需要對多個復制集進行集群,形成水平擴展每個復制集只負責存儲整個數據集的一部分,這就是Redis的集群,其作用是提供在多個Redis節點間共享數據的程序集。
-
Redis集群是一個提供在多個Redis節點間共享數據的程序集
- Redis集群可以支持多個Master
- Redis集群支持多個Master,每個Master又可以掛載多個Slave
- 讀寫分離
- 支持海量數據的高可用
- 支持海量數據的讀寫存儲操作
- 由于Cluster自帶Sentinel的故障轉移機制,內置了高可用的支持,無需再去使用哨兵功能
- 客戶端和Redis的節點連接,不再需要連接集群中所有節點,只需連接集群中的任意一個可用節點即可
- 槽位slot負責分配到各個物理服務節點,由對應的集群來負責維護節點、插槽和數據之間的關系
10.2 Redis集群分布式存儲
Redis集群分布式存儲有大概有3種解決方法
- 哈希取余分區
- 一致性哈希算法分區
- 哈希槽分區
哈希取余分區
hash(key) % N個機器臺數,計算出哈希值,用來決定數據映射到哪一個節點上。
優點:
- 簡單粗暴,直接有效,只需要預估好數據規劃節點例如3臺、8臺、10臺,就能保證一段時間的數據支撐。使用Hash算法讓固定的一部分請求落到同一臺服務器上,這樣每臺服務器固定處理一部分請求(并維護這些請求的信息),起到負載均衡+分而治之的作用。
缺點:
- 直接規劃好節點,進行擴容或者縮容會很麻煩,不管擴還是縮,每次數據變動會導致節點有變動,映射關系都要重新計算,在服務器個數固定不變時沒有問題。
- 如果需要彈性擴容或故障停機的情況下,原來的取模公式就會發生變化,Hash(key)/3會變成Hash(key) /?。此時地址經過取余運算的結果將發生很大變化,根據公式獲取的服務器也會變得不可控。
- 某個redis機器宕機了,由于臺數數量變化,會導致hash取余全部數據重新洗牌。
一致性哈希算法分區
提出一致性Hash解決方案。目的是當服務器個數發生變動時,盡量減少影響客戶端到服務器的映射關系
三個步驟
算法構建一致性哈希環
- 一致性哈希算法必然有個hash函數并按照算法產生hash值,這個算法的所有可能哈希值會構成一個全量集,這個集合可以成為一個hash空間[0,2^32-1],這個是一個線性空間,但是在算法中,我們通過適當的邏輯控制將它首尾相連(0 = 2^32),這樣讓它邏輯上形成了一個環形空間。
- 它也是按照使用取模的方法,前面筆記介紹的節點取模法是對節點(服務器)的數量進行取模。而一致性Hash算法是對232取模,簡單來說,一致性Hash算法將整個哈希值空間組織成一個虛擬的圓環,如假設某哈希函數H的值空間為0-232-1(即哈希值是一個32位無符號整形),整個哈希環如下圖:整個空間按順時針方向組織,圓環的正上方的點代表0,0點右側的第一個點代表1,以此類推,2、3、4、……直到232-1,也就是說0點左側的第一個點代表232-1, 0和232-1在零點中方向重合,我們把這個由232個點組成的圓環稱為Hash環。
服務器IP節點映射
-
將集群中各個IP節點映射到環上的某一個位置。
-
將各個服務器使用Hash進行一個哈希,具體可以選擇服務器的IP或主機名作為關鍵字進行哈希,這樣每臺機器就能確定其在哈希環上的位置。假如4個節點NodeA、B、C、D,經過IP地址的哈希函數計算(hash(ip)),使用IP地址哈希后在環空間的位置如下:
key 落到服務器的落鍵規則
-
當我們需要存儲一個kv鍵值對時,首先計算key的hash值,hash(key),將這個key使用相同的函數Hash計算出哈希值并確定此數據在環上的位置,從此位置沿環順時針“行走”,第一臺遇到的服務器就是其應該定位到的服務器,并將該鍵值對存儲在該節點上。
-
如我們有Object A、Object B、Object C、Object D四個數據對象,經過哈希計算后,在環空間上的位置如下:根據一致性Hash算法,數據A會被定為到Node A上,B被定為到Node B上,C被定為到Node C上,D被定為到Node D上。
優點
-
一致性哈希算法的容錯性
-
假設Node C宕機,可以看到此時對象A、B、D不會受到影響,只有C對象被重定位到Node D。一般的,在一致性Hash算法中,如果一臺服務器不可用,則受影響的數據僅僅是此服務器到其環空間中前一臺服務器(即沿著逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響。簡單說,就是C掛了,受到影響的只是B、C之間的數據,并且這些數據會轉移到D進行存儲。
-
-
一致性哈希算法的擴展性
-
數據量增加了,需要增加一臺節點NodeX,X的位置在A和B之間,那收到影響的也就是A到X之間的數據,重新把A到X的數據錄入到X上即可,
不會導致hash取余全部數據重新洗牌。
-
缺點
-
一致性哈希算法的數據傾斜問題
-
Hash環的數據傾斜問題
一致性Hash算法在服務節點太少時,容易因為節點分布不均勻而造成數據傾斜(被緩存的對象大部分集中緩存在某一臺服務器上)問題,
例如系統中只有兩臺服務器:
-
哈希槽分區
-
哈希槽實質就是一個數組,數組[0,2^14 -1]形成hash slot空間。
-
解決均勻分配的問題,在數據和節點之間又加入了一層,把這層稱為哈希槽(slot),用于管理數據和節點之間的關系,現在就相當于節點上放的是槽,槽里放的是數據。
-
槽解決的是粒度問題,相當于把粒度變大了,這樣便于數據移動。哈希解決的是映射問題,使用key的哈希值來計算所在的槽,便于數據分配
-
一個集群只能有16384個槽,編號0-16383(0-2^14-1)。這些槽會分配給集群中的所有主節點,分配策略沒有要求。
-
集群會記錄節點和槽的對應關系,解決了節點和槽的關系后,接下來就需要對key求哈希值,然后對16384取模,余數是幾key就落入對應的槽里。HASH_SLOT = CRC16(key) mod 16384。以槽為單位移動數據,因為槽的數目是固定的,處理起來比較容易,這樣數據移動問題就解決了。
-
當需要在 Redis 集群中放置一個 key-value時,redis先對key使用crc16算法算出一個結果然后用結果對16384求余數[ CRC16(key) % 16384],這樣每個 key 都會對應一個編號在 0-16383 之間的哈希槽,也就是映射到某個節點上。如下代碼,key之A 、B在Node2, key之C落在Node3上
| 如何找到給定key的分片 | 為了找到給定key的分片,我們對key進行CRC16(key)算法處理并通過對總分片數量取模。然后,使用確定性哈希函數,這意味著給定的key將多次始終映射到同一個分片,我們可以推斷將來讀取特定key的位置。 |
優勢:
缺點:
Redis集群不保證 強一致性,這意味著在特定的條件下,Redis集群可能會丟掉一些被系統收到的寫入請求命令
10.3 為什么redis集群的最大槽數是16384
-
Redis集群并沒有使用一致性hash而是引入了哈希槽的概念。Redis 集群有16384個哈希槽,每個key通過CRC16校驗后對16384取模來決定放置哪個槽,集群的每個節點負責一部分hash槽。
- 正常的心跳數據包帶有節點的完整配置,可以用冪等方式用舊的節點替換舊節點,以便更新舊的配置。這意味著它們包含原始節點的插槽配置,該節點使用2k的空間和16k的插槽,但是會使用8k的空間(使用65k的插槽)。同時,由于其他設計折衷,Redis集群不太可能擴展到1000個以上的主節點。因此16k處于正確的范圍內,以確保每個主機具有足夠的插槽,最多可容納1000個矩陣,但數量足夠少,可以輕松地將插槽配置作為原始位圖傳播。請注意,在小型群集中,位圖將難以壓縮,因為當N較小時,位圖將設置的slot / N位占設置位的很大百分比。
**(1)如果槽位為65536,發送心跳信息的消息頭達8k,發送的心跳包過于龐大。**在消息頭中最占空間的是myslots[CLUSTER_SLOTS/8]。 當槽位為65536時,這塊的大小是: 65536÷8÷1024=8kb
在消息頭中最占空間的是myslots[CLUSTER_SLOTS/8]。 當槽位為16384時,這塊的大小是: 16384÷8÷1024=2kb
因為每秒鐘,redis節點需要發送一定數量的ping消息作為心跳包,如果槽位為65536,這個ping消息的消息頭太大了,浪費帶寬。
(2)redis的集群主節點數量基本不可能超過1000個。
集群節點越多,心跳包的消息體內攜帶的數據越多。如果節點過1000個,也會導致網絡擁堵。因此redis作者不建議redis cluster節點數量超過1000個。 那么,對于節點數在1000以內的redis cluster集群,16384個槽位夠用了。沒有必要拓展到65536個。
(3)槽位越小,節點少的情況下,壓縮比高,容易傳輸
Redis主節點的配置信息中它所負責的哈希槽是通過一張bitmap的形式來保存的,在傳輸過程中會對bitmap進行壓縮,但是如果bitmap的填充率slots / N很高的話(N表示節點數),bitmap的壓縮率就很低。 如果節點數很少,而哈希槽數量很多的話,bitmap的壓縮率就很低。
10.4 集群配置(三主三從)
1.準備三臺虛擬機,各自新建文件目放配置文件
mkdir -p /myredis/cluster2.新建6個獨立的redis實例服務 (一臺虛擬機兩個)因為多個太過于占內存了,也可以用6個服務器
-
第一臺虛擬機IP: 192.168.238.111 + 端口 6381 / 6382
-
端口6381 (記著將密碼改成自己平常用的)
vim /myredis/cluster/redisCluster6381.conf bind 0.0.0.0 daemonize yes protected-mode no port 6381 logfile "/myredis/cluster/cluster6381.log" pidfile /myredis/cluster6381.pid dir /myredis/cluster dbfilename dump6381.rdb appendonly yes appendfilename "appendonly6381.aof" requirepass 123456 masterauth 123456cluster-enabled yes cluster-config-file nodes-6381.conf cluster-node-timeout 5000 -
端口 6382
vim /myredis/cluster/redisCluster6382.conf bind 0.0.0.0 daemonize yes protected-mode no port 6382 logfile "/myredis/cluster/cluster6382.log" pidfile /myredis/cluster6382.pid dir /myredis/cluster dbfilename dump6382.rdb appendonly yes appendfilename "appendonly6382.aof" requirepass 123456 masterauth 123456cluster-enabled yes cluster-config-file nodes-6382.conf cluster-node-timeout 5000
-
-
第二臺虛擬機IP: 192.168.238.112 + 端口 6383 / 6384
-
端口 6383
vim /myredis/cluster/redisCluster6383.conf bind 0.0.0.0 daemonize yes protected-mode no port 6383 logfile "/myredis/cluster/cluster6383.log" pidfile /myredis/cluster6383.pid dir /myredis/cluster dbfilename dump6383.rdb appendonly yes appendfilename "appendonly6383.aof" requirepass 123456 masterauth 123456cluster-enabled yes cluster-config-file nodes-6383.conf cluster-node-timeout 5000 -
端口 6384
vim /myredis/cluster/redisCluster6384.conf bind 0.0.0.0 daemonize yes protected-mode no port 6384 logfile "/myredis/cluster/cluster6384.log" pidfile /myredis/cluster6384.pid dir /myredis/cluster dbfilename dump6384.rdb appendonly yes appendfilename "appendonly6384.aof" requirepass 123456 masterauth 123456cluster-enabled yes cluster-config-file nodes-6384.conf cluster-node-timeout 5000
-
-
第三臺虛擬機IP: 192.168.238.113 + 端口 6385 / 6386
-
端口 6385
vim /myredis/cluster/redisCluster6385.conf bind 0.0.0.0 daemonize yes protected-mode no port 6385 logfile "/myredis/cluster/cluster6385.log" pidfile /myredis/cluster6385.pid dir /myredis/cluster dbfilename dump6385.rdb appendonly yes appendfilename "appendonly6385.aof" requirepass 111111 masterauth 111111cluster-enabled yes cluster-config-file nodes-6385.conf cluster-node-timeout 5000 -
端口 6386
vim /myredis/cluster/redisCluster6386.conf bind 0.0.0.0 daemonize yes protected-mode no port 6386 logfile "/myredis/cluster/cluster6386.log" pidfile /myredis/cluster6386.pid dir /myredis/cluster dbfilename dump6386.rdb appendonly yes appendfilename "appendonly6386.aof" requirepass 111111 masterauth 111111cluster-enabled yes cluster-config-file nodes-6386.conf cluster-node-timeout 5000
-
3.啟動6臺redis實例
redis-server /myredis/cluster/redisCluter6381.conf .......... redis-server /myredis/cluster/redisCluter6386.conf4.通過redis-cli命令為6臺機器構建集群關系
-
構建主從關系 注意用自己的IP
redis-cli -a 123456 --cluster create --cluster-replicas 1 192.168.238.111:6381 192.168.238.111:6382 192.168.238.112:6383 192.168.238.112:6384 192.168.238.113:6385 192.168.238.113:6386- -cluster-replicas 1 表示為每個master創建一個slave節點
5.任意連接一個作為切入點(集群只需要連一個),并檢驗集群狀態
-
這里連6381端口
redis-cli -a 123456 -p 6381 -c // -c表示集群 不加的話不是按照集群啟動的,對于在別的機器上的key,會報錯- 加 -c 啟動
-
不加 -c 啟動 ,在存值時,會報錯
-
查看節點狀態
- cluster nodes // 查看集群的主從關系
- cluster info // 查看集群信息
- info replication // 查看主從
6.測試一下集群
-
新增兩個key,看看集群是否成功
-
查看這個key的槽位值
cluster keyslot k2
7.主從容錯切換遷移
在我這里我的6381是slave,他的master是6384(在 cluster nodes 中 看他們的id)
- 把6384停了,6381會成為master
-
啟動6384,6381還是master,并不會讓位
-
Redis集群不保證強一致性,意味著在特定的條件下,Redis集群可能會丟掉一些被系統收到的寫入請求命令
- 因為本質還是發送心跳包,需要一些時間判斷是否down機,如果down機,對應的slave直接成為master
-
如果想要原先的master繼續做master的話
- CLUSTER FAILOVER // 讓誰上位 就在誰的端口號下執行這個命令
8.主從擴容
-
新建6387、6388 兩個服務實例配置文件+啟動 (又加了個虛擬機 或者 直接在三個虛擬機里選一個)
-
6387端口
- bind 0.0.0.0 daemonize yes protected-mode no port 6387 logfile "/myredis/cluster/cluster6387.log" pidfile /myredis/cluster6387.pid dir /myredis/cluster dbfilename dump6387.rdb appendonly yes appendfilename "appendonly6387.aof" requirepass 123456 masterauth 123456cluster-enabled yes cluster-config-file nodes-6387.conf cluster-node-timeout 5000
-
6388 端口
- bind 0.0.0.0 daemonize yes protected-mode no port 6388 logfile "/myredis/cluster/cluster6388.log" pidfile /myredis/cluster6388.pid dir /myredis/cluster dbfilename dump6388.rdb appendonly yes appendfilename "appendonly6388.aof" requirepass 123456 masterauth 123456cluster-enabled yes cluster-config-file nodes-6388.conf cluster-node-timeout 5000
-
-
啟動,此時這兩個實例都是master
- redis-server /myredis/cluster/redisCluster6388.conf redis-server /myredis/cluster/redisCluster6387.conf
-
將新增的6387節點作為master加入原集群
- redis-cli -a 123456 --cluster add-node 192.168.230.114:6387 192.168.238.111:6381
-
檢查集群情況,6381
- redis-cli -a 123456 --cluster check 192.168.238.111:6381
-
分配槽號,上圖沒有分配槽
- redis-cli -a 123456 --cluster reshard 192.168.238.111:6381
重新分配成本太高,所以前3家各自勻出來一部分,從6381/6383/6385三個舊節點分別勻出1364個坑位,注意本機這里經過調整所以我是需要從6381中分出4096即可
-
做的時候如果slots都一樣的話,直接用all,所有節點均勻分配給新節點
- redis-cli -a 123456 --cluster reshard 192.168.238.111:6381
-
再次檢查集群情況
- redis-cli -a 123456 --cluster check 192.168.238.111:6381
-
為主節點6387分配從節點6388 –cluster-master-id 后跟的是6387的id
- redis-cli -a 123456 --cluster add-node 192.168.238.114:6388 192.168.238.114:6387 --cluster-slave --cluster-master-id b861764cbba16a1b21536a3182349748e56f24cc
-
查看集群信息
9.主從縮容
讓6388和6387下線
-
先獲得6388的節點id(上圖可獲取),在集群中將6388刪除
- redis-cli -a 123456 --cluster del-node 192.168.238.114:6388 411144d66f28e876de5a6433689c8cbfab10686f
- 檢查節點,只剩7臺
-
將6387的槽號情況,重新分配,先全部都給6381
- redis-cli -a 123456 --cluster reshard 192.168.238.111:6381
-
查看集群情況
成功!
10.5 補充
總結
以上是生活随笔為你收集整理的Redis7之集群(十)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IE7.0 中设置选项卡中的在新的窗口中
- 下一篇: 智子社区顾问白硕:未来是属于智能媒体的