1. Redis Cluster介紹
Redis Cluster是Redis的分布式解決方案构挤,在Redis 3.0版本正式推出的,有效解決了Redis分布式方面的需求。當(dāng)遇到單機內(nèi)存孔轴、并發(fā)、流量等瓶頸時碎捺,可以采用Cluster架構(gòu)達(dá)到負(fù)載均衡的目的路鹰。
1.1 數(shù)據(jù)分布理論
分布式數(shù)據(jù)庫首要解決把整個數(shù)據(jù)集按照分區(qū)規(guī)則映射到多個節(jié)點的問題,即把數(shù)據(jù)集劃分到多個節(jié)點上牵寺,每個節(jié)點負(fù)責(zé)整個數(shù)據(jù)的一個子集悍引。常見的分區(qū)規(guī)則有哈希分區(qū)和順序分區(qū)。Redis Cluster采用哈希分區(qū)規(guī)則帽氓,因此接下來會討論哈希分區(qū)規(guī)則趣斤。常見的哈希分區(qū)有以下幾種:
- 節(jié)點取余分區(qū)
- 一致性哈希分區(qū)
- 虛擬槽分區(qū)
Redis Cluster采用虛擬槽分區(qū),因此先介紹一下虛擬槽分區(qū)黎休。
虛擬槽分區(qū)巧妙地使用了哈吓欤空間,使用分散度良好的哈希函數(shù)把所有的數(shù)據(jù)映射到一個固定范圍內(nèi)的整數(shù)集合势腮,整數(shù)定義為槽(slot)联贩。比如Redis Cluster槽的范圍是0 ~ 16383。槽是集群內(nèi)數(shù)據(jù)管理和遷移的基本單位捎拯。采用大范圍的槽的主要目的是為了方便數(shù)據(jù)的拆分和集群的擴展泪幌,每個節(jié)點負(fù)責(zé)一定數(shù)量的槽。
1.2 Redis 數(shù)據(jù)分區(qū)
Redis Cluster采用虛擬槽分區(qū),所有的鍵根據(jù)哈希函數(shù)映射到0 ~ 16383祸泪,計算公式:slot = CRC16(key)&16383吗浩。每一個節(jié)點負(fù)責(zé)維護(hù)一部分槽以及槽所映射的鍵值數(shù)據(jù)。
下圖展現(xiàn)一個五個節(jié)點構(gòu)成的集群没隘,每個節(jié)點平均大約負(fù)責(zé)3276個槽懂扼,以及通過計算公式映射到對應(yīng)節(jié)點的對應(yīng)槽的過程。
Redis虛擬槽分區(qū)的特定:
- 解耦數(shù)據(jù)和節(jié)點之間的關(guān)系右蒲,簡化了節(jié)點擴容和收縮難度阀湿。
- 節(jié)點自身維護(hù)槽的映射關(guān)系,不需要客戶端或者代理服務(wù)維護(hù)槽分區(qū)元數(shù)據(jù)瑰妄。
- 支持節(jié)點陷嘴、槽、鍵之間的映射查詢翰撑,用于數(shù)據(jù)路由罩旋、在線伸縮等場景。
1.3 Redis 集群功能限制
Redis集群相對單機在功能上有一定限制眶诈。
- key批量操作支持有限涨醋。如:MSET``MGET,目前只支持具有相同slot值的key執(zhí)行批量操作逝撬。
- key事務(wù)操作支持有限浴骂。支持多key在同一節(jié)點上的事務(wù)操作,不支持分布在多個節(jié)點的事務(wù)功能宪潮。
- key作為數(shù)據(jù)分區(qū)的最小粒度溯警,因此不能將一個大的鍵值對象映射到不同的節(jié)點。如:hash狡相、list梯轻。
- 不支持多數(shù)據(jù)庫空間。單機下Redis支持16個數(shù)據(jù)庫尽棕,集群模式下只能使用一個數(shù)據(jù)庫空間喳挑,即db 0。
- 復(fù)制結(jié)構(gòu)只支持一層滔悉,不支持嵌套樹狀復(fù)制結(jié)構(gòu)伊诵。
2. docker容器方式搭建 Redis Cluster
搭建集群工作分為三步:
- 打包生成redis的鏡像
- 創(chuàng)建Redis容器
- 節(jié)點握手
- 分配槽
2.1 打包生成redis的鏡像
Redis 集群一般由多個節(jié)點組成,用docker運行即為多個容器組成回官。節(jié)點或者容器數(shù)量為6個才能保證組成完整高可用的集群曹宴。下面打包redis的鏡像的Dockerfile文件:
# Redis
# Version 3.2.6
FROM redis:3.2.6
# MAINTAINER_INFO
MAINTAINER liano 837448792@qq.com
RUN mkdir -p /config
RUN mkdir -p /log
ENV REDIS_CONFIG /config
ENV REDIS_BIN /bin
ADD redis.conf $REDIS_CONFIG
RUN cp /usr/local/bin/redis-server $REDIS_BIN
WORKDIR /data
VOLUME ["/log","/data"]
EXPOSE 8000
ENTRYPOINT ["/bin/redis-server", "/config/redis.conf"]
這里使用了8000端口,暫且沒有使用6379端口歉提,這個可以自行指定笛坦。其中用到的redis.conf定義如下:
#設(shè)置為守護(hù)進(jìn)程
daemonize no
#Redis運行的進(jìn)程pid文件
pidfile /log/redis-8000.pid
#Redis服務(wù)端口號
port 8000
#Redis服務(wù)綁定ip
#bind 192.168.100.144
bind 0.0.0.0
#最大內(nèi)存
maxmemory 8gb
#開啟集群模式
cluster-enabled yes
#節(jié)點配置文件
cluster-config-file nodes-8000.conf
#集群節(jié)點超時時間(單位:毫秒)
cluster-node-timeout 15000
#集群是否需要所有的slot都分配給在線節(jié)點区转,才能正常訪問
cluster-require-full-coverage no
#工作目錄(aof、rdb版扩、日志文件)
dir /data
#tcp-backlog
tcp-backlog 511
#客戶端閑置多少秒后關(guān)閉連接(單位:秒)
timeout 300
#檢測TCP連接活性的周期(單位:秒)
tcp-keepalive 60
#日志級別
loglevel verbose
#日志記錄目錄
logfile "/log/redis-8000.log"
#可用的數(shù)據(jù)庫數(shù)
databases 16
#RDB保存條件
save 900 1
save 300 10
save 60 10000
#bgsave執(zhí)行錯誤蜗帜,是否停止Redis接受請求
stop-writes-on-bgsave-error no
#RDB文件是否壓縮
rdbcompression yes
#RDB文件是否使用校驗和
rdbchecksum yes
#RDB文件名
dbfilename dump-8000.rdb
#當(dāng)從節(jié)點與主節(jié)點連接中斷時,如果此參數(shù)值設(shè)置為“yes”资厉,從節(jié)點可以繼續(xù)處理客戶端的
#請求。否則除info和slaveof命令之外蔬顾,拒絕的所有請求并統(tǒng)一回復(fù)"SYNC with master in #progress"
slave-serve-stale-data yes
#從節(jié)點是否開啟只讀模式宴偿,集群架構(gòu)下從節(jié)點默認(rèn)讀寫都不可用,需要調(diào)用readyonly命令#開啟只讀模式
slave-read-only yes
#是否開啟無盤復(fù)制
repl-diskless-sync no
#開啟無盤復(fù)制后诀豁,需要延遲多少秒后進(jìn)行創(chuàng)建RDB操作窄刘,一般用于同時加入多個從節(jié)點時,#保證多個從節(jié)點可共享RDB
repl-diskless-sync-delay 5
#是否開啟主從復(fù)制socket的NO_DELAY選項:yes:Redis會合并小的TCP包來節(jié)省帶寬舷胜,但##是這樣增加同步延遲娩践,造成主#從數(shù)據(jù)不一致;no:主節(jié)點會立即發(fā)送同步數(shù)據(jù)烹骨,沒有延遲
repl-disable-tcp-nodelay no
#從節(jié)點的優(yōu)先級
slave-priority 100
#是否開啟AOF持久化模式
appendonly no
#Lua腳本“超時時間”(單位:毫秒)
lua-time-limit 5000
#慢查詢被記錄的閥值(單位微秒)
slowlog-log-slower-than 10000
#最多記錄慢查詢的條數(shù)
slowlog-max-len 1000
#Redis服務(wù)內(nèi)存延遲監(jiān)控
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
#是否激活重置哈希
activerehashing yes
#客戶端輸出緩沖區(qū)限制
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 512mb 128mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
#復(fù)制積壓緩存區(qū)大小
repl-backlog-size 256mb
#redis server執(zhí)行后臺任務(wù)的頻率,默認(rèn)為10
hz 10
#最大客戶端連接數(shù)
maxclients 15000
也可以直接pull我打好的鏡像:
docker pull registry.cn-hangzhou.aliyuncs.com/smallsoup/node-redis:0.4
由于是容器方式運行翻伺,所以redis.conf配置可以完全一樣:
#設(shè)置為守護(hù)進(jìn)程
daemonize no
#Redis運行的進(jìn)程pid文件
pidfile /log/redis-8000.pid
#Redis服務(wù)端口號
port 8000
#Redis服務(wù)綁定ip
#bind 192.168.100.144
bind 0.0.0.0
#最大內(nèi)存
maxmemory 8gb
#開啟集群模式
cluster-enabled yes
#節(jié)點配置文件
cluster-config-file nodes-8000.conf
#集群節(jié)點超時時間(單位:毫秒)
cluster-node-timeout 15000
#集群是否需要所有的slot都分配給在線節(jié)點,才能正常訪問
cluster-require-full-coverage no
2.2 創(chuàng)建Redis容器
創(chuàng)建Redis容器只需要使用第一步下載好的Redis鏡像沮焕,調(diào)用Docker的run命令即可:
docker run -it -d --name redis-master1 registry.cn-hangzhou.aliyuncs.com/smallsoup/node-redis:0.4
docker run -it -d --name redis-master2 registry.cn-hangzhou.aliyuncs.com/smallsoup/node-redis:0.4
docker run -it -d --name redis-master3 registry.cn-hangzhou.aliyuncs.com/smallsoup/node-redis:0.4
docker run -it -d --name redis-slave1 registry.cn-hangzhou.aliyuncs.com/smallsoup/node-redis:0.4
docker run -it -d --name redis-slave2 registry.cn-hangzhou.aliyuncs.com/smallsoup/node-redis:0.4
docker run -it -d --name redis-slave3 registry.cn-hangzhou.aliyuncs.com/smallsoup/node-redis:0.4
容器啟動完畢后吨岭,可以運行docker ps -a查看容器啟動狀態(tài):
可以查看日志文件:
有日志文件可得,節(jié)點已經(jīng)啟動成功峦树。這個日志文件是Redis服務(wù)器普通的日志文件辣辫,在集群模式下,第一次也會自動創(chuàng)建一個日志文件魁巩,由配置文件cluster-config-file指定文件急灭。
集群配置文件的作用:當(dāng)集群內(nèi)節(jié)點發(fā)生信息變化時,如添加節(jié)點谷遂、節(jié)點下線葬馋、故障轉(zhuǎn)移等。節(jié)點會自動保存集群的狀態(tài)到配置文件中埋凯。該配置文件由Redis自行維護(hù)点楼,不要手動修改,防止節(jié)點重啟時產(chǎn)生集群信息錯亂白对。
我們來查看一下掠廓,集群模式的日志文件:(以下截圖是3主3從搭建好之后的截圖。容器剛創(chuàng)建后進(jìn)入容器看到的會有不同)
也可以通過客戶端連接該節(jié)點甩恼,通過命令CLUSTER NODES來查看:
2.3 節(jié)點握手
節(jié)點握手是指一批運行在集群模式的節(jié)點通過Gossip協(xié)議彼此通信蟀瞧,達(dá)到感知對方的過程沉颂。節(jié)點握手是集群彼此通信的第一步,由客戶端發(fā)起命令:cluster meet <ip> <port>悦污,可以使用docker inpect <容器id>查看所有容器的IP地址:
在master1(容器IP為 172.17.0.2)的容器內(nèi)執(zhí)行以下命令铸屉,使得master1和master2、master3切端、salve1彻坛、salve2、salve3通信:
root@c121ba5c04c7:/data# redis-cli -h 127.0.0.1 -p 8000
127.0.0.1:8000> CLUSTER MEET 172.17.0.4 8000
OK
127.0.0.1:8000> CLUSTER MEET 172.17.0.3 8000
OK
127.0.0.1:8000> CLUSTER MEET 172.17.0.5 8000
OK
127.0.0.1:8000> CLUSTER MEET 172.17.0.6 8000
OK
127.0.0.1:8000> CLUSTER MEET 172.17.0.7 8000
OK
這樣可以讓所有的節(jié)點都互相感知踏枣〔耄可以通過cluster nodes命令查看。
2.4 分配槽
可以看一下8000端口的槽個數(shù)
127.0.0.1:8000> CLUSTER INFO
cluster_state:fail
cluster_slots_assigned:0 // 被分配槽的個數(shù)為0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:0
cluster_current_epoch:5
cluster_my_epoch:1
cluster_stats_messages_sent:479
cluster_stats_messages_received:479
接下來為節(jié)點分配槽空間茵瀑。通過cluster addslots命令间驮,可在master1容器內(nèi)執(zhí)行:
root@c121ba5c04c7:/data# redis-cli -h 127.0.0.1 -p 8000 cluster addslots {0..5461}
OK
root@c121ba5c04c7:/data# redis-cli -h 172.17.0.3 -p 8000 cluster addslots {5462..10922}
OK
root@c121ba5c04c7:/data# redis-cli -h 172.17.0.4 -p 8000 cluster addslots {10923..16383}
我們將16383個槽平均分配給master1、master2马昨、master3的節(jié)點(容器)竞帽。再次執(zhí)行CLUSTER INFO查看一下集群的狀態(tài):
127.0.0.1:6379> CLUSTER INFO
cluster_state:ok // 集群狀態(tài)OK
cluster_slots_assigned:16384 // 已經(jīng)分配了所有的槽
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:5
cluster_my_epoch:1
cluster_stats_messages_sent:1212
cluster_stats_messages_received:1212
可以通過CLUSTER NODES來查看分配情況。
127.0.0.1:8000> cluster nodes
31a8fc4031f74ea131632369406d257a60eff668 172.17.0.4:8000 master - 0 1552396086679 2 connected 10923-16383
c0404f497843cfd886f1c56f44759d74dac4fe36 172.17.0.5:8000 master - 0 1552396089697 0 connected
bd1086a2b9812492bd10031981df27f792d59073 172.17.0.2:8000 myself,master - 0 0 4 connected 0-5461
bd894d71f720098abc8c2c63675daa45a879f09e 172.17.0.6:8000 master - 0 1552396090704 5 connected
55f3c9e85ae393efd83551f69db2f05f1f8c7060 172.17.0.7:8000 master - 0 1552396087685 3 connected
4b61235b5ad5139e9a39f2b3608c64951b200c6f 172.17.0.3:8000 master - 0 1552396088689 1 connected 5462-10922
目前還有三個節(jié)點(容器)沒有使用鸿捧,作為一個完整的集群屹篓,每個負(fù)責(zé)處理槽的節(jié)點應(yīng)該具有從節(jié)點,保證當(dāng)主節(jié)點出現(xiàn)故障時匙奴,可以自動進(jìn)行故障轉(zhuǎn)移抱虐。集群模式下,首次啟動的節(jié)點和被分配槽的節(jié)點都是主節(jié)點饥脑,從節(jié)點負(fù)責(zé)復(fù)制主節(jié)點槽的信息和相關(guān)數(shù)據(jù)恳邀。
使用cluster replicate <nodeid>在從節(jié)點(容器)上執(zhí)行,在slave1灶轰、salve2谣沸、savle3上分別執(zhí)行:
#salve1容器內(nèi)執(zhí)行
redis-cli -h 127.0.0.1 -p 8000 cluster replicate bd1086a2b9812492bd10031981df27f792d59073
#salve2容器內(nèi)執(zhí)行
redis-cli -h 127.0.0.1 -p 8000 cluster replicate 31a8fc4031f74ea131632369406d257a60eff668
#salve3容器內(nèi)執(zhí)行
redis-cli -h 127.0.0.1 -p 8000 cluster replicate 4b61235b5ad5139e9a39f2b3608c64951b200c6f
這樣就完成了一個3主3從的Redis集群搭建。如下圖所示:
遇到的問題:
搭建完成后笋颤,驗證主從復(fù)制乳附,在容器內(nèi)執(zhí)行g(shù)et、set操作有問題伴澄,報錯:
(error)MOVED 15495 172.17.0.4::8000
原因
這種情況一般是因為啟動redis-cli時沒有設(shè)置集群模式所導(dǎo)致赋除。
解決方案
啟動時使用-c參數(shù)來啟動集群模式,命令如下:
redis-cli -c -h 172.17.0.2 -p 8000
測試OK
可以看到非凌,測試主從復(fù)制成功举农。在master容器中輸入set a b命令,在其他容器內(nèi)可以get到敞嗡。
參考:
Redis 學(xué)習(xí)筆記(十四)Redis Cluster介紹與搭建
(error) MOVED 原因和解決方案
Docker:創(chuàng)建Redis集群