准备工作
在 github 上下载 redis 源码,编译
./configure
make
LZ 下载的 redis 版本是 3.0.7
编译成功后,在源码的根目录下的 src 目录中,会生成对应的可执行文件
redis-server redis 服务器启动程序
redis-cli redis客户端程序
redis-trib.rb 这是一个ruby程序,可以用于创建集群、检查、删除节点等
redis-sentinel 哨兵程序,能够监控redis HA和主备主机
创建集群
在 redis 官网也介绍了集群的搭建方法 [https://redis.io/topics/cluster-tutorial]
LZ 共使用了四个虚拟机,用于创建集群,每天虚拟机三个节点,分别是一个 RHEL,三个Ubuntu Server 16.04,IP地址分别如下:
192.168.192.166,以下简称166
192.168.192.132,以下简称132
192.168.192.133,以下简称133
192.168.192.134,以下简称134
创建节点
在 166 主机创建 config/clusters/
目录,再在这个目录下创建 7001, 7002 和 7003 三个目录,分别以目录名称作为端口号(前提是你的机器上这些端口号没有被占用, netstat -pnl
查看端口号)
创建一个通用配置文件 redis-common.conf
daemonize yes
tcp-backlog 511
timeout 0
tcp-keepalive 0
loglevel debug #开放最大日志级别,能够通过日志信息查看更多细节
database 16
slave-serve-stale-data yes #redis在复制的时候也可访问
#slave 只读
slave-read-only yes
repl-disable-tcp-nodelay yes
slave-priority 100
#打开 aof
appendonly yes
appendfsync everysec #在不清楚的这个配置的情况,redis 推荐设置这个参数为 everysec
no-appendfsync-on-rewrite yes #关闭aof rewite的时候对新的写操作进行 fsync
auto-aof-rewrite-min-size 64mb
lua-time 5000
#打开redis集群
cluster-enabled yes
#节点互联超时时的阀值
cluster-node-timeout 15000
cluster-migration-barrier 1
#slow log
slowlog-log-slower-than 10000
slowlog-max-len 128
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
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
再根据不同的节点,为每一个节点创建一个特殊的配置文件,比如 redis-7001.conf
,并通过 include 包含通用配置
include redis-common.conf
port 7001
bind 192.168.192.166
logfile "" #日志文件保存路径+日志文件名,建议方便的话,可以保存在对应的节点目录下
dir "" #rdb, aof ,nodes.conf 文件的保存路径
appendfilename "appendonly-7001.aof"
cluster-config-file "nodes-7001.conf" #这个配置文件是由redis自动创建的,里面记录的集群节点的一些信息,比如master 节点,slave 节点的IP,端口号以及状态(是否failed)
maxmemory 100m
maxmemory-policy allkeys-lru #内存耗尽时的淘汰策略
#remove any keys according to the LRU algorithm
auto-aof-rewrite-percentage 100
为每一个虚拟机创建好三个节点之后,就开始启用节点
启动节点
166主机
redis-server redis-7001.conf
redis-server redis-7002.conf
redis-server redis-7003.conf
132主机
redis-server redis-7004.conf
redis-server redis-7005.conf
redis-server redis-7006.conf
133主机
redis-server redis-7007.conf
redis-server redis-7008.conf
redis-server redis-7009.conf
134主机
redis-server redis-7010.conf
redis-server redis-7011.conf
redis-server redis-7012.conf
节点启动后,查看日志信息,观察是否均启动成功
创建集群
redis cluster 在设计的时候,就考虑到了去中心化,去中间件,也就是说,集群中的每个节点都是平等的关系,都是对等的,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。
Redis 集群没有并使用传统的一致性哈希来分配数据,而是采用另外一种叫做哈希槽 (hash slot)的方式来分配的。redis cluster 默认分配了 16384 个slot,当我们set一个key 时,会用CRC16算法来取模得到所属的slot,然后将这个key 分到哈希槽区间的节点上,具体算法就是:CRC16(key) % 16384。
Redis 集群会把数据存在一个 master 节点,然后在这个 master 和其对应的salve 之间进行数据同步。当读取数据时,也根据一致性哈希算法到对应的 master 节点获取数据。只有当一个master 挂掉之后,才会启动一个对应的 salve 节点,充当 master 。
需要注意的是:必须要3个或以上的主节点,否则在创建集群时会失败,并且当存活的主节点数小于总节点数的一半时,整个集群就无法提供服务了。
当所有的节点都创建好后,在 src 目录中有一个文件 redis-trib.rb
(前面介绍了这个文件),用这个工具创建集群。
Usage: redis-trib <command> <options> <arguments ...>
set-timeout host:port milliseconds
del-node host:port node_id
reshard host:port
--from <arg>
--pipeline <arg>
--yes
--timeout <arg>
--slots <arg>
--to <arg>
create host1:port1 ... hostN:portN
--replicas <arg>
help (show this help)
info host:port
check host:port
call host:port command arg arg .. arg
add-node new_host:new_port existing_host:existing_port
--master-id <arg>
--slave
rebalance host:port
--weight <arg>
--threshold <arg>
--pipeline <arg>
--auto-weights
--simulate
--timeout <arg>
--use-empty-masters
fix host:port
--timeout <arg>
import host:port
--replace
--from <arg>
--copy
For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.
上面是这个工具的用法,该文件是使用 ruby 编写的,首先在你的环境中需要安装 ruby
RHEL 中安装
yum install ruby
Ubuntu 中安装
sudo apt-get install ruby
LZ因为环境比较无语,主机无法连接网络,所以都是使用的本地仓库的形式,具体如何创建网络仓库,可以去找找资料,这里不多说,Ubuntu 创建本地仓库比较简单,就是挂在一个镜像,然后使用 apt-cdrom,在 source.list 文件中就会增加一个源,指向的就是挂在的镜像目录。
安装好 ruby 之后,执行 redis-trib.rb
文件可能仍会报错
`require`: no such file to load -- rubygems (LoadError)
`require`: no such file to load -- redis (LoadError)
这个是因为你的环境中没有安装 rubygems 和 ruby redis (在没网的环境中,手动下载依赖包简直就是噩梦)
下载 rubygems: [https://rubygems.org/page/download]
ruby redis: [https://rubygems.org/gems/redis],这是一个ruby 的redis 客户端
下载好 rubygems 后,执行
ruby setup.rb --help
能够查看详细的细节,安装执行 gem
ruby setup.rb
安装完成之后,即可安装 ruby redis 客户端了
gem install -l redis-3.2.1.gem
OK,环境配置好后,开始创建集群
./redis-trib.rb create --replicas 2 192.168.192.166:7001 192.168.192.166:7002 192.168.192.166:7003 192.168.192.132:7004 192.168.192.132:7005 192.168.192.132:7006 192.168.192.133:7007 192.168.192.133:7008 192.168.192.133:7009 192.168.192.134:7010 192.168.192.134:7011 192.168.192.134:7012
创建12个节点,4个 master 节点,每一个 master 节点有 2 个 slave 节点。
如下图所示表示成功
集群操作
1. 查看节点信息
使用 redis-trib.rb info IP:PORT
./redis-trib.rb info 192.168.192.166:7001
2. 检查集群状态
使用 redis-trib.rb check IP:PORT
3. 停止其中一个master,再启动,观察变化
当前集群运行状态是 OK 的
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:12
cluster_size:4
cluster_current_epoch:17
cluster_my_epoch:1
cluster_stats_messages_sent:43924
cluster_stats_messages_received:11595
一共12个节点,有4个master节点,在 7001 的节点下,查看 node-7001.conf 这个文件(这个文件是 redis 自动生成的)
这个文件记录了集群中所有相关的能够到达的节点的信息,包括master的状态(是否是failed)
我们来看一下,每一个master节点都有哪些slave,info replication
能够清晰的查看slave 或者 master 的信息
master: 192.168.192.166 7003
ip:192.168.192.133,port:7007
ip:192.168.192.134,port:7012
master: 192.168.192.166 7002
ip:192.168.192.132,port:7005
master: 192.168.192.132 7004
ip:192.168.192.132,port:7006
ip:192.168.192.133,port:7009
master: 192.168.192.166 7001
ip:192.168.192.133,port:7008
ip:192.168.192.134,port:7011
这是当前我们的集群分布,上面的是 master,下面的两个是该 master 的两个 slave
现在我们来关闭其中一个 master,7004
redis-cli -h 192.168.192.132 -p 7004
shutdown
关闭之后再来查看 node-7001.conf 这个文件信息,此时发现,有一个 master 显示的是 failed
当 master failed 的时候,redis 会从它的从服务器中选一个作为新的 master,然后另一个从服务器将作为新的 master 的 slave,再来看集群分布
master: 192.168.192.132 7006
ip:192.168.192.133,port:7009
master: 192.168.192.166 7003
ip:192.168.192.133,port:7007
ip:192.168.192.134,port:7012
master: 192.168.192.166 7002
ip:192.168.192.132,port:7005
master: 192.168.192.166 7001
ip:192.168.192.133,port:7008
ip:192.168.192.134,port:7011
在没有停止 7004 之前,7004 作为 master,7006 和 7009 作为它的 slave,在将 7004 停止之后,7006 成为了新的 master,并且 7009 变成了它的 slave,当我们再次重新启动 7004 时,redis 不会重新把它作为 master,它将作为新的 master 7006 的一个 slave
4. 集群失败
redis 集群必须要有三个或以上的 master 节点,否则集群将创建失败,目前集群的状态时 ok,如果我们关闭 166 和 132 主机,查看集群状态为 fail
5. 添加新的 master 节点
在 132 主机上创建 8000 节点,在 133 主机上创建 8001 节点 (配置文件与其他节点的相同,只需要修改一下相应的文件保存路径即可)
启动节点
redis-server redis-8001.conf
redis-server redis-8000.conf
添加节点
./redis-trib.rb add-node 192.168.192.132:8000 192.168.192.133:7004
出现如下错误
[ERR] Node 192.168.192.132:8000 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.
这个节点之前添加了一次,没有添加成功,导致节点非空,添加失败,解决办法:
1) 登陆数据库,将数据进行删除 flushdb
2) 将节点下的集群配置文件删除,即 node-8000.conf,redis 自动生成的文件
3) 删除节点下的 aof , rdb 文件等本地备份文件
再次添加节点
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 192.168.192.132:8000 to make it join the cluster.
[OK] New node added correctly.
节点添加成功。查看集群的状态发现节点数成了13个
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:13
cluster_size:4
cluster_current_epoch:24
cluster_my_epoch:0
cluster_stats_messages_sent:284
cluster_stats_messages_received:284
当前集群的分布如下所示
master: 192.168.192.132 7005
ip:192.168.192.134,port:7010
ip:192.168.192.166,port:7002
master: 192.168.192.132 7006
ip:192.168.192.133,port:7009
ip:192.168.192.132,port:7004
master: 192.168.192.166 7003
ip:192.168.192.134,port:7012
ip:192.168.192.133,port:7007
master: 192.168.192.132 8000
master: 192.168.192.166 7001
ip:192.168.192.133,port:7008
ip:192.168.192.134,port:7011
新添加的 8000 端口是 master 节点,它不包含任何数据,因为没有包含任何的 slot。新添加的都为主节点,当集群需要将某个节点升级为新的主节点时,这个节点不会被选中。
为新节点分配 slot
redis-trib.rb rehashed 192.168.192.132:8000
按照提示操作
#选择要迁移的 slot 数量
How many slots do you want to move (from 1 to 16384)? 500
What is the receiving node ID? 81e71239d85d4112e87c5fe00d513f879c980118
#all 表示从所有的 master 重新分配
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1:all
Ready to move 500 slots.
Source nodes:
M: 030197fec21c3192c0f8938ad276f8c8062c6410 192.168.192.166:7001
slots:0-4095 (4096 slots) master
2 additional replica(s)
M: c6e30b67bc201f4f48bdf5fe9d88209505e2f0d9 192.168.192.132:7005
slots:4096-8191 (4096 slots) master
2 additional replica(s)
M: 70d2ed1edd1035972927a7c8415ca9d0c941d21d 192.168.192.166:7003
slots:8192-12287 (4096 slots) master
2 additional replica(s)
M: c63aac7105f9601915e65ef0d3ff45b0b3f0a7ec 192.168.192.132:7006
slots:12288-16383 (4096 slots) master
2 additional replica(s)
M: 1a260ae8c08a9ee1b4d856e0fcf190c271590cf0 192.168.192.133:8001
slots: (0 slots) master
0 additional replica(s)
Destination node:
M: 81e71239d85d4112e87c5fe00d513f879c980118 192.168.192.132:8000
slots: (0 slots) master
0 additional replica(s)
Resharding plan:
Moving slot 0 from 030197fec21c3192c0f8938ad276f8c8062c6410
......
Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 0 from 192.168.192.166:7001 to 192.168.192.132:8000:
......
6. 添加新的 slave 节点
节点添加与上面添加 master 的前面步骤相同
1. 创建节点目录和节点配置文件,启动节点
2. redis-trib.rb add-node new-ip:new-port exist_ip:exist_port
3. 这样,添加上的节点为 master 节点,然后使用 redis-cli 连接,输入命令
cluster replicate node-id
即添加成功,node-id 可以通过查看命令 CLUSTER NODES
查看,也可以直接查看节点的 node.conf
文件
NOTE:在线添加 slave 节点时,需要 dump 整个 master 进程,并传递到 slave,再由 slave 加载 rdb 文件到内存, rdb 传输过程中可能无法提供服务,整个过程需要消耗大量的I/O。
7. 删除 master 节点
删除节点之前,需要使用 reshard 移除该 master 的全部 slot,然后才能删除节点,接收 slot 的节点必须是 master 节点
./redis-trib.rb reshard 192.168.192.134:8003
How many slots do you want to move (from 1 to 16384)? 500
What is the receiving node ID? c6023463d9f33e4c5ca72fdc85eb4c7d95b258a5
*** The specified node is not known or not a master, please retry.
What is the receiving node ID? 1a260ae8c08a9ee1b4d856e0fcf190c271590cf0
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1:81e71239d85d4112e87c5fe00d513f879c980118
Source node #2:done
Ready to move 500 slots.
Source nodes:
M: 81e71239d85d4112e87c5fe00d513f879c980118 192.168.192.132:8000
slots:0-124,4096-4220,8192-8316,12288-12412 (500 slots) master
1 additional replica(s)
Destination node:
M: 1a260ae8c08a9ee1b4d856e0fcf190c271590cf0 192.168.192.133:8001
slots: (0 slots) master
0 additional replica(s)
Resharding plan:
Moving slot 0 from 81e71239d85d4112e87c5fe00d513f879c980118
......
移除所有的 slot 之后,删除空 master 节点
redis-trib.rb del-node 192.168.192.132:8000 '81e71239d85d4112e87c5fe00d513f879c980118'
8. 删除 slave 节点
#redis-trib.rb del-node ip:port 'node-id'