Redis Cluster是Redis的分布式解決方案七芭,在3.0版本正式推出匪凉。當(dāng)遇到單機內(nèi)存、并發(fā)蹬叭、流量等瓶頸時藕咏,可 以采用Cluster架構(gòu)方案達(dá)到負(fù)載均衡的目的。
想直接看簡單的安裝秽五,直接拉到最后面看C語言的工具安裝孽查。不過我建議整體看一遍,然后選擇使用工具安裝
1. 幾個概念
1.1 數(shù)據(jù)分布理論
分布式數(shù)據(jù)庫首先要解決把整個數(shù)據(jù)集按照分區(qū)規(guī)則映射到多個節(jié)點的 問題坦喘,即把數(shù)據(jù)集劃分到多個節(jié)點上卦碾,每個節(jié)點負(fù)責(zé)整體數(shù)據(jù)的一個子集铺坞。一般分為哈希分區(qū)和順序分區(qū):
- 哈希分區(qū)
- 離散度好;數(shù)據(jù)分布與業(yè)務(wù)無關(guān)洲胖;無法順序訪問济榨;
- 代表有:Redis Cluster;Cassandra;Dynamo;
- 順序分區(qū)
- 離散容易傾斜绿映;數(shù)據(jù)分布業(yè)務(wù)相關(guān)擒滑;可順序訪問
- 代表有: Bigtable; HBase;Hypertable;
1.2 常見的Hash分區(qū)規(guī)則
節(jié)點區(qū)域分區(qū)
使用特定的數(shù)據(jù),比如Redis的key叉弦,然后通過公式hash(key)%N計算出哈希值丐一,在決定映射到哪個節(jié)點;一致性哈希分區(qū)
為系統(tǒng)中每個節(jié) 點分配一個token淹冰,范圍一般在0~232库车,這些token構(gòu)成一個哈希環(huán)。數(shù)據(jù)讀寫 執(zhí)行節(jié)點查找操作時樱拴,先根據(jù)key計算hash值柠衍,然后順時針找到第一個大于 等于該哈希值的token節(jié)點。-
虛擬槽分區(qū)
虛擬槽分區(qū)巧妙地使用了哈暇牵空間珍坊,使用分散度良好的哈希函數(shù)把所有 數(shù)據(jù)映射到一個固定范圍的整數(shù)集合中,整數(shù)定義為槽(slot)正罢。這個范圍 一般遠(yuǎn)遠(yuǎn)大于節(jié)點數(shù)阵漏,比如Redis Cluster槽范圍是0~16383。槽是集群內(nèi)數(shù)據(jù) 管理和遷移的基本單位翻具。采用大范圍槽的主要目的是為了方便數(shù)據(jù)拆分和集 群擴展履怯。每個節(jié)點會負(fù)責(zé)一定數(shù)量的槽。
當(dāng)前集群有5個節(jié)點裆泳,每個節(jié)點平均大約負(fù)責(zé)3276個槽叹洲。由于采用高質(zhì) 量的哈希算法,每個槽所映射的數(shù)據(jù)通常比較均勻晾虑,將數(shù)據(jù)平均劃分到5個 節(jié)點進行數(shù)據(jù)分區(qū)疹味。Redis Cluster就是采用虛擬槽分區(qū)。
Redis Cluser采用虛擬槽分區(qū)帜篇,所有的鍵根據(jù)哈希函數(shù)映射到0~16383整 數(shù)槽內(nèi)糙捺,計算公式:slot=CRC16(key)&16383。每一個節(jié)點負(fù)責(zé)維護一部 分槽以及槽所映射的鍵值數(shù)據(jù)
2. 搭建集群
2.1 普通搭建
這里只是為了演示笙隙,所以集群的主在一臺機子上洪灯,備在一臺機子上,按理來說竟痰,是需要在不同的機子上的签钩,這個主要在節(jié)點握手的時候IP控制就可以了掏呼。
2.1.1. 準(zhǔn)備節(jié)點
- 安裝redis
因為是CentOS,所以可以通過yum安裝,yum install redis
安裝完成后铅檩,redis的配置文件在/etc下面憎夷,但是暫時使用 - 準(zhǔn)備3個redis文件
分別使用6910, 6911, 6912端口,在/etc/redis文件夾下
# redis-6910.conf
bind 0.0.0.0
port 6910
#logfile=/data/redis/logs/redis-6910.log
logfile /data/redis/logs/redis-6910.log
dir /data/redis/data/6910
dbfilename dump.rdb
daemonize yes
pidfile /var/run/redis_6910.pid
# 密碼
requirepass 123456
# 集群配置
cluster-enabled yes
# 節(jié)點超時時間,單位毫秒
cluster-node-timeout 1500
# 集群內(nèi)部配置文件
cluster-config-file /data/redis/conf/node-6910.conf
# redis-6911.conf
bind 0.0.0.0
port 6911
logfile /data/redis/logs/redis-6911.log
dir /data/redis/data/6911
dbfilename dump.rdb
daemonize yes
pidfile /var/run/redis_6911.pid
# 密碼
requirepass 123456
# 集群配置
cluster-enabled yes
# 節(jié)點超時時間昧旨,單位毫秒
cluster-node-timeout 1500
# 集群內(nèi)部配置文件
cluster-config-file /data/redis/conf/node-6911.conf
# redis-6912.conf
bind 0.0.0.0
port 6912
logfile /data/redis/logs/redis-6912.log
dir /data/redis/data/6912
dbfilename dump.rdb
daemonize yes
pidfile /var/run/redis_6912.pid
# 密碼
requirepass 123456
# 集群配置
cluster-enabled yes
# 節(jié)點超時時間拾给,單位毫秒
cluster-node-timeout 1500
# 集群內(nèi)部配置文件
cluster-config-file /data/redis/conf/node-6912.conf
配置文件準(zhǔn)備好后,啟動redis兔沃,分別執(zhí)行以下命令:
redis-server /etc/redis/redis-6910.conf
redis-server /etc/redis/redis-6911.conf
redis-server /etc/redis/redis-6912.conf
在啟動之后蒋得,在/data/redis/conf/
下會有三個文件,這三個文件是集群的配置文件乒疏,不要手動更改额衙,在集群發(fā)生變化的時候,會自動去更新:
5ede6b4b1368afa21bc400968e614e6ed68d1d44 127.0.0.1:6910 myself,master - 0 0 2 connected
2.1.2. 節(jié)點握手
登錄其中一臺redis:redis-cli -h 127.0.0.1 -p 6910
登錄后通過密碼認(rèn)證:auth 123456
先查看集群狀態(tài):
cluster nodes
####顯示內(nèi)容怕吴,只有一臺#####
5ede6b4b1368afa21bc400968e614e6ed68d1d44 :6910 myself,master - 0 0 0 connected
節(jié)點握手
cluster meet 127.0.0.1 6911
cluster meet 127.0.0.1 6912
# 這時候再查看節(jié)點狀態(tài)
cluster nodes
####顯示內(nèi)容窍侧,有三臺了####
5ede6b4b1368afa21bc400968e614e6ed68d1d44 127.0.0.1:6910 myself,master - 0 0 2 connected
685509c1b4c3c765f459b336d4995dbca2f938ae 127.0.0.1:6912 master - 0 1607417226801 0 connected
0c04c840b911c96ad2f1e28f9c12323155d313f7 127.0.0.1:6911 master - 0 1607417227001 1 connected
# 查看集群狀態(tài)
cluster info
# 這時候集群是不可用的,禁止讀寫械哟,因為還沒有分配槽
###顯示內(nèi)容###
cluster_state:fail
cluster_slots_assigned:0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:3
cluster_size:0
cluster_current_epoch:2
cluster_my_epoch:2
cluster_stats_messages_sent:43
cluster_stats_messages_received:43
2.1.3. 分配槽
Redis集群把所有的數(shù)據(jù)映射到16384個槽中疏之。每個key會映射為一個固 定的槽殿雪,只有當(dāng)節(jié)點分配了槽暇咆,才能響應(yīng)和這些槽關(guān)聯(lián)的鍵命令。通過 cluster addslots命令為節(jié)點分配槽丙曙。
# 注意命令里是兩個.不是3個
redis-cli -h 127.0.0.1 -p 6910 -a 123456 cluster addslots {0..5461}
redis-cli -h 127.0.0.1 -p 6910 -a 123456 cluster addslots {5462..10922}
redis-cli -h 127.0.0.1 -p 6910 -a 123456 cluster addslots {10923..16383}
這時候再查看集群狀態(tài)爸业,發(fā)現(xiàn)已經(jīng)是可用了:
redis-cli -h 127.0.0.1 -p 6910 -a 123456
cluster info
# 顯示內(nèi)容
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:3
cluster_size:1
cluster_current_epoch:2
cluster_my_epoch:2
cluster_stats_messages_sent:14120
cluster_stats_messages_received:14120
2.1.4 備節(jié)點
- 同樣先準(zhǔn)備節(jié)點,我在另一臺服務(wù)器上準(zhǔn)備了3個redis亏镰,端口分別以6920, 6921, 6922來啟動扯旷,配置文件和上面的類似,這里放一個配置文件:
bind 0.0.0.0
port 6920
#logfile=/data/redis/logs/redis-6920.log
logfile /data/redis/logs/redis-6920.log
dir /data/redis/data/6920
dbfilename dump.rdb
daemonize yes
pidfile /var/run/redis_6920.pid
# 密碼
requirepass 123456
# 集群配置
cluster-enabled yes
# 節(jié)點超時時間索抓,單位毫秒
cluster-node-timeout 1500
# 集群內(nèi)部配置文件
cluster-config-file /data/redis/conf/node-6920.conf
- 啟動每臺redis:
redis-server /etc/redis/redis-6920.conf
redis-server /etc/redis/redis-6921.conf
redis-server /etc/redis/redis-6922.conf
- 加入到集群中钧忽,在之前已經(jīng)起來的redis集群中把這幾臺也加入進來,使用meet命令:
cluster meet 120.45.59.183 6920
cluster meet 120.45.59.183 6921
cluster meet 120.45.59.183 6922
# 查看節(jié)點狀態(tài)
cluster nodes
d5eac57d1e4755cc9b0d73f1e2a642020a44edb7 120.45.59.183:6922 master - 0 1607426875916 0 connected
1d3bdedff56c924f6f759c90c3cdab25535ac4a0 120.45.59.183:6921 master - 0 1607426875515 0 connected
5ede6b4b1368afa21bc400968e614e6ed68d1d44 127.0.0.1:6910 myself,master - 0 0 2 connected 0-16383
685509c1b4c3c765f459b336d4995dbca2f938ae 127.0.0.1:6912 master - 0 1607426875507 3 connected
ff2c70be220996e80eca74b4cd6f932412a880fa 120.45.59.183:6920 master - 0 1607426875716 0 connected
0c04c840b911c96ad2f1e28f9c12323155d313f7 127.0.0.1:6911 master - 0 1607426875908 1 connected
127.0.0.1:6910> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:1
cluster_current_epoch:3
cluster_my_epoch:2
cluster_stats_messages_sent:62442
cluster_stats_messages_received:62442
- 登錄每一個備節(jié)點redis逼肯,使用cluster replicate{nodeId}命令讓一個節(jié)點成為從節(jié)點耸黑。其中命令執(zhí)行必須在對應(yīng)的 從節(jié)點上執(zhí)行,nodeId是要復(fù)制主節(jié)點的節(jié)點ID
redis-cli -h 127.0.0.1 -p 6920 -a 123456
cluster replicate 5ede6b4b1368afa21bc400968e614e6ed68d1d44
redis-cli -h 127.0.0.1 -p 6921 -a 123456
cluster replicate 0c04c840b911c96ad2f1e28f9c12323155d313f7
redis-cli -h 127.0.0.1 -p 6922 -a 123456
cluster replicate 685509c1b4c3c765f459b336d4995dbca2f938ae
# 再查看節(jié)點情況篮幢,可以看到有主從節(jié)點了
cluster nodes
d5eac57d1e4755cc9b0d73f1e2a642020a44edb7 120.45.59.183:6922 slave 685509c1b4c3c765f459b336d4995dbca2f938ae 0 1607427288089 3 connected
1d3bdedff56c924f6f759c90c3cdab25535ac4a0 120.45.59.183:6921 slave 0c04c840b911c96ad2f1e28f9c12323155d313f7 0 1607427288289 1 connected
5ede6b4b1368afa21bc400968e614e6ed68d1d44 127.0.0.1:6910 myself,master - 0 0 2 connected 0-16383
685509c1b4c3c765f459b336d4995dbca2f938ae 127.0.0.1:6912 master - 0 1607427288481 3 connected
ff2c70be220996e80eca74b4cd6f932412a880fa 120.45.59.183:6920 slave 5ede6b4b1368afa21bc400968e614e6ed68d1d44 0 1607427288490 2 connected
0c04c840b911c96ad2f1e28f9c12323155d313f7 127.0.0.1:6911 master - 0 1607427288681 1 connected
到此為止大刊,集群就搭建完畢了。如果要更多的備節(jié)點或者主節(jié)點三椿,都可以類似的添加缺菌。但是在搭建完成后葫辐,數(shù)據(jù)落進去之后再進行擴容,那就要進行槽遷移伴郁、數(shù)據(jù)遷移等一系列工作了耿战。這里先不說了。
2.2 利用工具搭建
上面搭建的方式確實是可以把集群搭建起來焊傅,但是怎么看也是有些繁瑣的昆箕,所以會有使用工具搭建。
使用工具搭建的話可以使用redis-trib.rb租冠,這是使用Ruby開發(fā)的(Ruby是一個很有意思的語言)鹏倘。
- 安裝ruby的環(huán)境
這個環(huán)境在不同的系統(tǒng)又不同的方法,具體可以查看官網(wǎng)(中文網(wǎng):http://www.ruby-lang.org/zh_cn/documentation/installation/#yum)顽爹,比如CentOS可以選擇下載后編譯安裝纤泵,也可以通過yum安裝。但是不推薦镜粤,因為yum安裝的ruby版本很低捏题,下面只是一個示例,還是推薦編譯安裝肉渴。
# centOS
sudo yum install ruby
# macos
brew install ruby
在安裝完ruby之后公荧,就可以使用ruby的包管理工具gem,這個類似于yum的工具同规;通過ruby的gem工具安裝redis依賴(可能要等好久):
gem install redis
編譯安裝:
# 解決安裝的依賴庫
yum install gcc gcc-c++ gdbm-devel readline-devel openssl-devel wget -y
# 下載源碼包
wget https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.2.tar.gz
# 解壓
tar -zxvf ruby-2.7.2.tar.gz
# 編譯安裝
cd ruby-2.7.2/
./configure --prefix=/usr/local/ruby --enable-shared
make && make install
# 設(shè)置環(huán)境變量
vim /etc/profile
# 在最下面增加
export RUBY_HOME=/usr/local/ruby
export PATH=$PATH:$RUBY_HOME/bin:$RUBY_HOME/lib
# 保存后執(zhí)行
source /etc/profile
# 驗證安裝
ruby -v
gem -v
# 安裝ruby的redis庫,可能會有點慢
gem install redis
# 安裝后顯示內(nèi)容
Fetching redis-4.2.5.gem
Successfully installed redis-4.2.5
Parsing documentation for redis-4.2.5
Installing ri documentation for redis-4.2.5
Done installing documentation for redis after 0 seconds
1 gem installed
然后問題來了循狰,我的redis是yum安裝的,一直找不到redis-trib.rb券勺,然后我就又去下載了一個源碼版绪钥,在里面就找到了,下載的時候根據(jù)yum安裝的redis版本來下載的:
wget https://download.redis.io/releases/redis-3.2.12.tar.gz
# 解壓
tar -zxvf redis-3.2.12.tar.gz
# 復(fù)制文件
cp ./redis-3.2.12/src/redis-trib.rb /usr/bin
接下來同樣是要準(zhǔn)備節(jié)點,準(zhǔn)備節(jié)點后只要將每個節(jié)點啟動就行了关炼,然后節(jié)點握手和分配槽就是集群管理工具來做程腹。
準(zhǔn)備節(jié)點的步驟和之前是一樣的,如果已經(jīng)按照上面的步驟做過儒拂,那就把每一臺redis停掉寸潦,然后刪除掉redis的集群信息(/data/conf下的文件),再啟動就可以了
# 集群創(chuàng)建社痛,這里的--replicas 1的意思是一個從節(jié)點见转,然后后面是所有的redis的ip和端口,會自己分配槽褥影,自己握手
redis-trib.rb create --replicas 1 120.55.134.85:6910 120.55.134.85:6911 120.55.134.85:6912 122.58.124.87:6920 122.58.124.87:6921 122.58.124.87:6922
# 這時候我遇到了2個問題池户,1個是用的云服務(wù)器,沒有對自己放開端口,也就是自己訪問自己的公網(wǎng)IP訪問不了校焦,導(dǎo)致創(chuàng)建失斏薅丁;
一個是之前我設(shè)置了redis密碼寨典,創(chuàng)建集群的時候不支持密碼氛雪,所以先要把密碼去掉啟動后再設(shè)置。當(dāng)然也可以通過修改ruby源碼連設(shè)置密碼
# 集群創(chuàng)建后的顯示內(nèi)容
Using 3 masters:
120.55.134.85:6910
122.58.124.87:6920
120.55.134.85:6911
Adding replica 122.58.124.87:6921 to 120.55.134.85:6910
Adding replica 120.55.134.85:6912 to 122.58.124.87:6920
Adding replica 122.58.124.87:6922 to 120.55.134.85:6911
M: 66d6f3ef1523226d64b4ef3baaf8b12f07f961e6 120.55.134.85:6910
slots:0-5460 (5461 slots) master
M: 3640a0e3f5c2607053ace32a1e2f192a13b96b48 120.55.134.85:6911
slots:10923-16383 (5461 slots) master
S: 26f7831c0e1ee4ec2fab2987e57fbe02ae050934 120.55.134.85:6912
replicates 7799daaec5f3ce5c3b2c1f64446fc133d1f20de6
M: 7799daaec5f3ce5c3b2c1f64446fc133d1f20de6 122.58.124.87:6920
slots:5461-10922 (5462 slots) master
S: 60f1726c401c977c9c1ef4610dd6db05a3688f89 122.58.124.87:6921
replicates 66d6f3ef1523226d64b4ef3baaf8b12f07f961e6
S: 46d3c7a05979e29edd34b53cf3138d331805f818 122.58.124.87:6922
replicates 3640a0e3f5c2607053ace32a1e2f192a13b96b48
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 120.55.134.85:6910)
M: 66d6f3ef1523226d64b4ef3baaf8b12f07f961e6 120.55.134.85:6910
slots:0-5460 (5461 slots) master
1 additional replica(s)
S: 26f7831c0e1ee4ec2fab2987e57fbe02ae050934 120.55.134.85:6912
slots: (0 slots) slave
replicates 7799daaec5f3ce5c3b2c1f64446fc133d1f20de6
S: 46d3c7a05979e29edd34b53cf3138d331805f818 122.58.124.87:6922
slots: (0 slots) slave
replicates 3640a0e3f5c2607053ace32a1e2f192a13b96b48
M: 7799daaec5f3ce5c3b2c1f64446fc133d1f20de6 122.58.124.87:6920
slots:5461-10922 (5462 slots) master
1 additional replica(s)
M: 3640a0e3f5c2607053ace32a1e2f192a13b96b48 120.55.134.85:6911
slots:10923-16383 (5461 slots) master
1 additional replica(s)
S: 60f1726c401c977c9c1ef4610dd6db05a3688f89 122.58.124.87:6921
slots: (0 slots) slave
replicates 66d6f3ef1523226d64b4ef3baaf8b12f07f961e6
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 加上密碼
# 登錄每一個節(jié)點耸成,每個節(jié)點密碼需要一樣
redis-cli -h 122.58.124.87 -p 6920
config set masterauth 123456
config set requirepass 123456
auth 123456
config rewrite
到這里為止报亩,使用工具搭建集群也就完成了。
但是井氢,為了搭建一個集群還要裝一個ruby的環(huán)境弦追,有點蠢是不是?
所以在redis5.x的版本中花竞,搭建集群的工具就是使用C去寫的了劲件,不用再通過ruby環(huán)境去搭建了,所以接下來寫一下redis5.x的集群搭建方式:
2.3 redis5.x利用工具搭建集群
首先约急,yum安裝redis默認(rèn)安裝的還是3.x的版本零远,我們需要安裝redis5.x或以上版本,一種是通過編譯安裝厌蔽,還有一種是修改存儲庫的方式牵辣,那就2個方式都說一下,一臺修改存儲庫奴饮,一臺通過編譯安裝:
1. 通過修改存儲庫安裝
# 安裝redis4/5版本通過IUS存儲庫(僅支持redhat/centos)
# 1. 安裝 epel repo
yum install -y epel-release bash-completion
# 2. 安裝 IUS repo
yum install -y https://repo.ius.io/ius-release-el7.rpm https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
# 3. 安裝 redis5
yum install -y redis5
然后向上面一樣纬向,啟動就可以了:redis-server /etc/redis/redis-6910.conf
2. 編譯安裝
# 安裝依賴包
yum -y install gcc
yum -y install libc
# 下載
wget https://download.redis.io/releases/redis-5.0.10.tar.gz?_ga=2.194157549.1777432635.1607479744-1380992581.1607479744
# 解壓安裝
tar -zxvf wget https://download.redis.io/releases/redis-5.0.10.tar.gz?_ga=2.194157549.1777432635.1607479744-1380992581.1607479744
mv redis-5.0.10/ /usr/bin/redis
cd /usr/bin/redis
make && make install
# 安裝完畢
然后向之前一樣,啟動各個節(jié)點:
redis-server /etc/redis/redis-6920.conf
3. 使用redis5中自帶的集群管理工具創(chuàng)建集群
redis-cli --cluster create 120.55.134.85:6910 120.55.134.85:6911 120.55.134.85:6912 122.58.124.87:6920 122.58.124.87:6921 122.58.124.87:6922 --cluster-replicas 1
# 詳細(xì)的使用可以搜索redis-cli --cluster
# 然后每個節(jié)點設(shè)置密碼拐云,我是推薦集群也是要加上密碼的
config set masterauth 123456
config set requirepass 123456
auth 123456
config rewrite
到此為止履磨,redis5.x利用自帶的C語言寫的集群管理器搭建也就完成了
3. SpringBoot使用
1. 添加相關(guān)的依賴
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
// 連接池需要的
implementation 'org.apache.commons:commons-pool2'
2. 配置文件添加redis集群信息
spring:
redis:
timeout: 6000ms
password: 123456
cluster:
max-redirects: 3 # 獲取失敗 最大重定向次數(shù)
nodes:
- 120.55.134.85:6910
- 120.55.134.85:6911
- 120.55.134.85:6912
- 122.58.124.87:6920
- 122.58.124.87:6921
- 122.58.124.87:6922
lettuce:
pool:
max-active: 1000 # 連接池最大連接數(shù)
max-idle: 10 # 連接池最大空閑數(shù)
min-idle: 5 # 連接池最小空閑數(shù)
max-wait: -1 # 連接池最大阻塞等待時間
3. 測試類編寫
@SpringBootTest
class RedisClusterDemoApplicationTests {
@Autowired
private StringRedisTemplate redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("hello3", "world");
redisTemplate.opsForValue().set("hugh3", "22");
redisTemplate.opsForValue().set("name3", "1990");
redisTemplate.opsForList().leftPush("number2", "001");
redisTemplate.opsForList().leftPush("number2", "002");
redisTemplate.opsForList().leftPush("number2", "003");
}
}