Redis 官方集群
Redis Cluster
官方文檔節選
文檔地址
1.automatically sharded
- The ability to automatically split your dataset among multiple nodes.
- The ability to continue operations when a subset of the nodes are experiencing failures or are unable to communicate with the rest of the cluster.
Redis Cluster集群是以分布式運行支持高達1000個節點,會將數據自動進行分割存儲在集群中的不同節點.除非是大部分的master節點宕機或不可訪問.
2.TCP ports
- The normal client communication port (usually 6379) used to communicate with clients to be open to all the clients that need to reach the cluster, plus all the other cluster nodes (that use the client port for keys migrations).
- The cluster bus port (the client port + 10000) must be reachable from all the other cluster nodes.
Redis Cluster集群服務使用了兩種TCP端口,會監聽一個普通端口和總線端口(bus port英語沒過級不知道怎麽翻譯好.),普通端口和其它模式下的6379端口一樣,用於客戶端與集群中節點交互.集群中的總線端口是用於集群節點之間故障檢測,配置更新,授權等等,如果總線端口不能通信集群也就不能連接到這個節點.總線端口總是固定在普通端口基礎上偏移10000.
3.Hash slot
Redis Cluster集群沒有使用一致性hash,而是使用的hash slot(哈希槽).每個集群總共有16384個哈希槽,集群會根據節點的數量分配給每個主節點一個哈希槽子集(即一部分哈希槽).因此可以動態增加和刪除節點,只需要重新分配哈希槽.同時啟用了一個稱為hash tags的概念,因此集群模式下只能使用database 0.(hash tags不懂...)
4.master-slave model
Redis Cluster集群也支持主從模式,必須保證主節點必須要有對應的一個或多個從節點,在主節點不能訪問的時候,其中一個從節點會提升為主節點.否則集群在主節點宕機後將無法工作.
5.consistency guarantees
Redis Cluster不能保證節點間良好的一致性,所以在某些極端情況下會出現數據不一致的情況.因為主從之間使用的異步寫入,並且不會等待確認返回.如果需要使用同步寫入的話可以使用WAIT命令.即使是同步寫入也有不能寫入的從節點被提升為主節點造成的寫入失敗的情況發生.(官方舉的這個例子和我遇到的一次mysql主備切換造成binlog丟失的情況很像,有一次生產環境的mysql主庫使用率100%持續很久,造成切換備用節點.然後這些數據只能在被切的那個機器上找到.其它的備庫,從庫通通沒有binlog日誌.)
集群配置
1.集群安裝配置
#Redis Cluster集群管理工具
yum install redis-trib -y
#gem install redis
- 創建配置文件及配置參數
- cluster-enabled <yes/no>: #集群是否啟用
- cluster-config-file
- cluster-node-timeout
- cluster-slave-validity-factor
- cluster-migration-barrier
- cluster-require-full-coverage <yes/no>: #節點通信密鑰相關
- 配置節點實例
mkdir /etc/redis.d/700{1,2,3,4,5,6} -p
echo /etc/redis.d/700{1,2,3,4,5,6} |xargs -n 1 cp -v /etc/redis.conf
#將每個節點的端口和cluster-conifg-file修改為不同
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
redis-server /etc/redis.d/7001/redis.conf
...
redis-server /etc/redis.d/7006/redis.conf
- 創建集群
執行創建集群的命令create ,replicas 1表示每個主節點分配一個從節點
[root@localhost ~]# redis-trib create --replicas 1 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006
2.增加和刪除節點
- 增加節點
查看節點信息
redis-cli -p 30001 cluster nodes
a2e26df91d65847a17f4172b0b61f840b0948c56 127.0.0.1:30003@40003 master - 0 1516419900548 3 connected 10923-16383
...
啟動實例主節點30007.從節點30008,
redis-server --port 30007 --cluster-enabled yes --cluster-config-file nodes-30007.conf --cluster-node-timeout 2000 --appendonly yes --appendfilename appendonly-30007.aof --dbfilename dump-30007.rdb --logfile 30007.log --daemonize yes
...
加入集群
redis-trib add-node 127.0.0.1:30007 127.0.0.1:30001
redis-trib add-node --slave --master-id d0617fb34ef85de4e414b62c4a82f8b3e0aa2e1f 127.0.0.1:30008 127.0.0.1:30001
查看節點信息,此時新加入的節點沒有分配hash slot所以是一個空節點並不參入集群的工作
redis-cli -c -p 30001 cluster nodes
...
65537824bef847f3f199f9b076a928d860f00c7a 127.0.0.1:30008@40008 slave d0617fb34ef85de4e414b62c4a82f8b3e0aa2e1f 0 1516423427008 0 connected
d0617fb34ef85de4e414b62c4a82f8b3e0aa2e1f 127.0.0.1:30007@40007 master - 0 1516421726020 0 connected
...
reshared重新分配hash slot
redis-trib reshard 127.0.0.1:30001 --slots 4096 --from all --to d0617fb34ef85de4e414b62c4a82f8b3e0aa2e1f --yes 127.0.0.1:30001
redis-cli -c -p 30001 cluster nodes
...
d0617fb34ef85de4e414b62c4a82f8b3e0aa2e1f 127.0.0.1:30007@40007 master - 0 1516423765000 8 connected 0-1364 5461-6826 10923-12287
#此時可以看到30007的slots是從其它三個主節點上平均分配了過來
故障測試,從節點會自動提升為主機點
redis-cli -p 30007 debug segfault
redis-cli -c -p 30001 cluster nodes
...
65537824bef847f3f199f9b076a928d860f00c7a 127.0.0.1:30008@40008 master - 0 1516424352174 9 connected 0-1364 5461-6826 10923-12287
d0617fb34ef85de4e414b62c4a82f8b3e0aa2e1f 127.0.0.1:30007@40007 master,fail - 1516424346060 1516424345046 8 disconnected
...
# 在重啟故障節點後,30007會自動加入為30008的從節點
刪除節點
redis-trib reshard 127.0.0.1:30001 --slots 4096 --from 65537824bef847f3f199f9b076a928d860f00c7a --to 3dda66a50f8b1f4601a94be6b7e110c04501017d --yes
redis-trib info 127.0.0.1:30007
127.0.0.1:30008@40008 (65537824...) -> 0 keys | 0 slots | 0 slaves.
127.0.0.1:30002@40002 (3dda66a5...) -> 98 keys | 8192 slots | 2 slaves
...
#此時30008的從節點也會自動歸屬到新的主節點上去
redis-trib del-node 127.0.0.1:30001 65537824bef847f3f199f9b076a928d860f00c7a
重新平衡slots
redis-trib rebalance --auto-weights 127.0.0.1:30001
redis-trib info 127.0.0.1:30001
127.0.0.1:30001 (09a3e281...) -> 68 keys | 5462 slots | 1 slaves.
127.0.0.1:30003@40003 (a2e26df9...) -> 67 keys | 5461 slots | 1 slaves.
127.0.0.1:30002@40002 (3dda66a5...) -> 63 keys | 5461 slots | 2 slaves.
集群遷移
做集群遷移的時候官方已經提供了很多意見.
1. Stop your clients. No automatic live-migration to Redis Cluster is currently possible. You may be able to do it orchestrating a live migration in the context of your application / environment.
2. Generate an append only file for all of your N masters using the BGREWRITEAOF command, and waiting for the AOF file to be completely generated.
3. Save your AOF files from aof-1 to aof-N somewhere. At this point you can stop your old instances if you wish (this is useful since in non-virtualized deployments you often need to reuse the same computers).
4. Create a Redis Cluster composed of N masters and zero slaves. You‘ll add slaves later. Make sure all your nodes are using the append only file for persistence.
5. Stop all the cluster nodes, substitute their append only file with your pre-existing append only files, aof-1 for the first node, aof-2 for the second node, up to aof-N.
6. Restart your Redis Cluster nodes with the new AOF files. They‘ll complain that there are keys that should not be there according to their configuration.
7. Use redis-trib fix command in order to fix the cluster so that keys will be migrated according to the hash slots each node is authoritative or not.
8. Use redis-trib check at the end to make sure your cluster is ok.
9. Restart your clients modified to use a Redis Cluster aware client library.
1.保存aof文件並復制.
redis-trib call 127.0.0.1:30001 BGREWRITEAOF
mkdir /opt/redis-test
...
2.啟動新的集群節點.
#修改腳本
vi redis-cluster.sh
PORT=10000
TIMEOUT=2000
NODES=3
REPLICAS=0
...
./redis-cluster.sh start
....
3.使用redis-cli配置集群(使用redis的原生命令)
分配slots
redis-cli -c -p 10001 cluster addslots {0..5460}
redis-cli -c -p 10002 cluster addslots {5461..10922}
redis-cli -c -p 10003 cluster addslots {10922..16383}
設置epoch
redis-cli -c -p 10001 cluster set-config-epoch 1
redis-cli -c -p 10002 cluster set-config-epoch 2
redis-cli -c -p 10003 cluster set-config-epoch 3
加入集群
redis-cli -c -p 10003 cluster meet 127.0.0.1 10001
redis-cli -c -p 10003 cluster meet 127.0.0.1 10002
redis-trib check 127.0.0.1:10001
>>> Performing Cluster Check (using node 127.0.0.1:10001)
M: d5a115418f657f4b25229af38b5b88036c90b3b4 127.0.0.1:10001
slots:0-5460 (5461 slots) master
0 additional replica(s)
4.復制aof文件
./redis-cluster.sh stop
cp ~/appendonly-30001.aof ./appendonly-10001.aof
...
./redis-cluster.sh start
5.修復集群
redis-trib fix 127.0.0.1:10001
redis-trib check 127.0.0.1:10001
6.查看數據
redis-test]# redis-cli -c -p 10001
127.0.0.1:10001> keys *
1) "foo96"
2) "foo56"
3) "foo67"
集群管理工具命令參數
1.redis-trib子命令
create host1:port1 ... hostN:portN #創建集群
--replicas <arg> #表示每個主節點的從節點數量
check host:port #檢查節點
info host:port #輸出主節點信息 keys,slots,slaves
fix host:port #修復集群,比較遷移後配置問題
--timeout <arg>
reshard host:port #分配slots
--from <arg> #源節點
--to <arg> #目標節點
--slots <arg> #slots數量
--yes #不使用交互模式
--timeout <arg>
--pipeline <arg> #定義每次cluster getkeysinslot取值,默認10
rebalance host:port #平衡slots
--weight <arg> #節點權重,多個節點指定多個
--auto-weights #自動分配權重
--use-empty-masters #是否啟用空節點參與
--timeout <arg>
--simulate #模擬操作
--pipeline <arg>
--threshold <arg> #設置slots平衡算法參數
add-node new_host:new_port existing_host:existing_port
--slave #新節點為從節點
--master-id <arg> #做為從節點復制的主節點ID
del-node host:port node_id
set-timeout host:port milliseconds
call host:port command arg arg .. arg #執行redis-cli命令
import host:port #導入數據
--from <arg>
--copy
--replace
help (show this help)
2.redis原生集群命令
cluster info #打印集群的信息
cluster nodes #列出集群當前已知的所有節點(node),以及這些節點的相關信息
節點(node)
cluster meet <ip> <port> #將ip和port所指定的節點添加到集群當中,讓它成為集群的一份子
cluster forget <node_id> #從集群中移除node_id指定的節點
cluster replicate <node_id> #將當前節點設置為node_id指定的節點的從節點
cluster saveconfig #將節點的配置文件保存到硬盤裏面
cluster slaves <node_id> #列出該slave節點的master節點
cluster set-config-epoch #強制設置configEpoch
槽(slot)
cluster addslots <slot> [slot ...] #將一個或多個槽(slot)指派(assign)給當前節點
cluster delslots <slot> [slot ...] #移除一個或多個槽對當前節點的指派
cluster flushslots #移除指派給當前節點的所有槽,讓當前節點變成一個沒有指派任何槽的節點
cluster setslot <slot> node <node_id> #將槽slot指派給node_id指定的節點,如果槽已經指派給另一個節點,那麽先讓另一個節點刪除該槽,然後再進行指派
cluster setslot <slot> migrating <node_id> #將本節點的槽slot遷移到node_id指定的節點中
cluster setslot <slot> importing <node_id> #從node_id 指定的節點中導入槽slot到本節點
cluster setslot <slot> stable #取消對槽slot的導入(import)或者遷移(migrate)
鍵(key)
cluster keyslot <key> #計算鍵key應該被放置在哪個槽上
cluster countkeysinslot <slot> #返回槽slot目前包含的鍵值對數量
cluster getkeysinslot <slot> <count> #返回count個slot槽中的鍵
其它
cluster myid #返回節點的ID
cluster slots #返回節點負責的slot
cluster reset #重置集群,慎用
3.啟動腳本
#!/bin/bash # Settings PORT=30000 TIMEOUT=2000 NODES=6 REPLICAS=1 # You may want to put the above config parameters into config.sh in order to # override the defaults without modifying this script. if [ -a config.sh ] then source "config.sh" fi # Computed vars ENDPORT=$((PORT+NODES)) if [ "$1" == "start" ] then while [ $((PORT < ENDPORT)) != "0" ]; do PORT=$((PORT+1)) echo "Starting $PORT" redis-server --port $PORT --cluster-enabled yes --cluster-config-file nodes-${PORT}.conf --cluster-node-timeout $TIMEOUT --appendonly yes --appendfilename appendonly-${PORT}.aof --dbfilename dump-${PORT}.rdb --logfile ${PORT}.log --daemonize yes done exit 0 fi if [ "$1" == "create" ] then HOSTS="" while [ $((PORT < ENDPORT)) != "0" ]; do PORT=$((PORT+1)) HOSTS="$HOSTS 127.0.0.1:$PORT" done redis-trib create --replicas $REPLICAS $HOSTS exit 0 fi if [ "$1" == "stop" ] then while [ $((PORT < ENDPORT)) != "0" ]; do PORT=$((PORT+1)) echo "Stopping $PORT" redis-cli -p $PORT shutdown nosave done exit 0 fi if [ "$1" == "watch" ] then PORT=$((PORT+1)) while [ 1 ]; do clear date redis-cli -p $PORT cluster nodes | head -30 sleep 1 done exit 0 fi if [ "$1" == "tail" ] then INSTANCE=$2 PORT=$((PORT+INSTANCE)) tail -f ${PORT}.log exit 0 fi if [ "$1" == "call" ] then while [ $((PORT < ENDPORT)) != "0" ]; do PORT=$((PORT+1)) redis-cli -p $PORT $2 $3 $4 $5 $6 $7 $8 $9 done exit 0 fi if [ "$1" == "clean" ] then rm -rf *.log rm -rf appendonly*.aof rm -rf dump*.rdb rm -rf nodes*.conf exit 0 fi if [ "$1" == "clean-logs" ] then rm -rf *.log exit 0 fi echo "Usage: $0 [start|create|stop|watch|tail|clean]" echo "start -- Launch Redis Cluster instances." echo "create -- Create a cluster using redis-trib create." echo "stop -- Stop Redis Cluster instances." echo "watch -- Show CLUSTER NODES output (first 30 lines) of first > node." echo "tail <id> -- Run tail -f of instance at base port + ID." echo "clean -- Remove all instances data, logs, configs." echo "clean-logs -- Remove just instances logs."
Redis 官方集群