Redis3.0
开始支持集群 Cluster
,redis cluster
集群,要求至少3个master
,去组成一个高可用,健壮的分布式的集群,每个master
都建议至少给一个slave
,3个master
,3个slave
。
正式环境下,建议都是说在6台机器上去搭建,这样可以保证master
死了,它的slave
可以掌管大权。
redis
节点内部使用二进制协议传送数据,通过 ping-pong
机制,判断节点是否存活redis
节点直接连接,不需要通过代理层,客户端连接集群中任意一个可用节点即可Redis-cluster
把所有的物理节点映射到[ 0-16383 ]slot
插槽上,cluster
负责维护 node <-> slot <-> value
准备3台虚拟机,我们需要3个master
和3个slave
,将redis.conf
配置文件分别改成7001.conf,7002.conf,7003.conf,7004.conf,7005.conf,7006.conf
,端口分别是7001、7002、7003、7004、7005、7006
3台虚拟机:
192.168.1.10 对应 7001.conf,7002.conf
192.168.1.11 对应 7003.conf,7004.conf
192.168.1.12 对应 7005.conf,7006.conf
创建文件夹:
mkdir -p /etc/redis-cluster
mkdir -p /var/log/redis
mkdir -p /var/redis/7001
修改配置文件的内容:一定不要设置密码
port 7001
cluster-enabled yes
# 每个conf配置与自己的端口一致
cluster-config-file /etc/redis-cluster/node-7001.conf
cluster-node-timeout 15000
daemonize yes
pidfile /var/run/redis_7001.pid
dir /var/redis/7001
logfile /var/log/redis/7001.log
bind 192.168.1.10
appendonly yes
创建集群关系需要用 redis-trib.rb
,而这个东西是 ruby
语言编写的,因此需要安装 ruby 环境。
yum -y install zlib ruby rubygems
gem install redis
如果执行 gem install redis
出现 redis requires Ruby version >= 2.2.2
的错误,那是因为Centos
默认支持 ruby2.0.0
,可gem
安装 redis
需要最低是2.2.2。
解决方法:
curl -sSL https://rvm.io/mpapis.asc | gpg2 --import -
curl -sSL https://get.rvm.io | bash -s stable
rvm list known
source /etc/profile.d/rvm.sh
rvm install 2.3.3
rvm use 2.3.3
rvm remove 2.0.0
ruby --version
gem install redis
创建集群前,需要将所有的服务启动,然后进入 redis
的 src
目录下,执行:
# 例如下面这个,配置了六个redis,replicas的值为1,那么在创建集群时,会自动的将这6个redis分组,一个master对应一个slave
# 注意:master和slave的对应关系是随机的
./redis-trib.rb create --replicas 1 192.168.1.10:7001 192.168.1.10:7002 192.168.1.11:7003 192.168.1.11:7004 192.168.1.12:7005 192.168.1.12:7006
# 验证集群是否可用
./redis-trib.rb check 192.168.1.12:7006
注意:命令中的 ip 不要使用 127.0.01,否则 jedis 客户端无法连接;配置文件中一定不要配置密码。
我们没有在配置文件中指定密码是因为创建集群的 redis-trib.rb 命令没有密码的参数,如果配置了密码,会出现:
在客户端中配置认证密码:
redis-cli -c -p 6379
config set masterauth 111111
config set requirepass 111111
config rewrite
exit
redis-cli -c -p 6379 -a 111111
ping
每个客户端都需要配置密码,并且各个节点密码都必须一致,否则在插入数据时,Redirected 就会失败, 推荐这种方式,这种方式会把密码写入到 redis.conf 里面去,且不用重启。
我们搭建的集群,7003和7002是一对master和slave,如果7003挂掉的话,7002会自动变成master,7003重启后会变成slave挂在7002名下,这个实验不做了。
# 必须要加上 -c 这个选项,否则redis在计算hash值后会出现问题
redis-cli -c -h 192.168.1.10 -p 7001
不加 -c
出现的问题
不加 -c
选项会出现如下问题,可以看到提示我们要在192.168.1.11的7003端口的客户端上执行,这是因为集群搭建时,master和slave是随机配对的,而我们又知道读写分离,slave只能读。
搭建集群时的截图中,redis自动配对master和slave,我的7003端口是master,它对应的slave是7002,在7003上info replication
查看主从信息
我们在7003的客户端上写入数据,然后可以获取数据,但是在其他的客户端上都会出现问题,并且它的slave上也会出现这个问题,但是在它的slave的客户端使用readonly
之后就可以获取数据,其他客户端使用这个命令无效
加上 -c
选项不会出现上述问题
数据要存在哪个节点上是根据 key 的有效部分计算的哈希值,再将哈希值对 16384 (0~16383)取余,得到插槽值。
key 的有效部分:
1)如果 key 中包含一对花括号,并且花括号之间至少有一个字符,那么这个花括号中的值就是 key 的有效部分,例如:key 的值是 {hello}_world,有效部分是 hello ,它的插槽值与单独一个 hello 得插槽值是一样的。
2)如果不满足条件1,那么整个 key 都是有效部分
新增节点:
./redis-trib.rb add-node 192.168.2.100:6382 192.168.168.2.100:6379 add-node
命令中的第一个 ip:port 是新增的节点的 ip 和 port,第二个 ip:port 是集群中已经存在的任意一个节点的 ip 和 port
分配插槽:
./redis-trib.rb reshard 192.168.2.100:6382
此命令运行过程中,需要执行一些操作:
1、 要从其他节点抽取多少个插槽给新增的节点
2、 新增的节点的id
3、 all:要从其他所有节点中抽取插槽
4、 要抽取插槽的其他节点的id+回车,换行后可以再选择另外的节点的id+回车(此处省略你想抽取的另外的一些节点的id+回车),输入done表示开始抽取
5、 3和4任选其一
6、 3和4步后输入yes表示完成
转移要删除的节点的插槽,以删除6380节点为例:
./redis-trib reshard 192.168.2.100:6380
1)输入转移插槽的数量
2)输入转移的节点的 id
3)输入 done 开始转移
删除节点:
./redis-trib.rb del-node 192.168.56.102:6380 4a9b8886ba5261e82597f5590fcdb49ea47c4c6c
故障机制:
1)集群中的每个节点会定期 ping 其他节点,通过有没有 pong 判断目标节点是否下线
2)集群中每一秒就会随机选择 5 个节点,选择其中最久没有响应的节点 进行 ping
3)当集群中节点超过半数人为目标节点疑似下线,那么节点被标记为下线
4)当集群中任何一个节点下线,导致插槽有空档、不完整,那么集群将不可用
解决方案:
1)使用主从模式实现各个节点的高可用
2)当某个节点(master)宕机后,集群会将该节点的 slave 变为 master 继续完成集群服务
1)集群中如果用 mset 和 mget 等多键命令时,如果每个 key 都位于一个节点,则可以正常支持,否则会提示错误
2)集群中的节点只能使用 0 号库,如果执行 select 切换数据库会提示错误
Usage: redis-trib
create host1:port1 ... hostN:portN
--replicas
check host:port
info host:port
fix host:port
--timeout
reshard host:port
--from
--to
--slots
--yes
--timeout
--pipeline
rebalance host:port
--weight
--auto-weights
--use-empty-masters
--timeout
--simulate
--pipeline
--threshold
add-node new_host:new_port existing_host:existing_port
--slave
--master-id
del-node host:port node_id
set-timeout host:port milliseconds
call host:port command arg arg .. arg
import host:port
--from
--copy
--replace
help (show this help)