一:简介
一:特点
Redis集群是一个可以在多个Redis节点之间进行数据共享的设施
Redis集群不支持那些需要同时处理多个键的Redis命令,因为执行这些命令需要在多个Redis节点之间移动数据,并且在高负载的情况下,会降低Redis集群的性能
Redis集群通过分区来提供一定程度的可用性,即使集群中有一部分节点失效或者无法继续通讯,集群也能继续处理命令请求,同时将数据切分到多个节点的能力
二:集群数据共享
一:定义
redis集群使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现,一个redis集群包含16384个哈希槽(hash slot),数据库中的每个键都属于这16384中的一个,其使用集群公式CRC16(key)%16384来计算key属于哪个槽位,其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
节点 A 负责处理 0 号至 5500 号哈希槽。 节点 B 负责处理 5501 号至 11000 号哈希槽。 节点 C 负责处理 11001 号至 16384 号哈希槽。
二:槽的计算公式
集群使用公式 CRC16(key) & 16383 计算键 key属于哪个槽。
三:集群运行机制
所有redis节点彼此互连(ping-pong机制),内部使用二进制协议优化传输速度和带宽
节点的fail是通过集群中超过半数的master节点检测失效时候才失效
客户端与节点之间相连,连接集群中任何一个可用的节点即可
把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->key
在redis集群中为了使得一部分节点在下线或者无法与集群大多数(majority)节点进行通讯的情况下,仍然可以正常运作,redis集群对节点使用主从复制功能,集群中的每个节点都有1至N个复制品,其中一个复制品为主节点(master),其余N-1为从节点(slave)
假设在上图中A B C三个节点中 因为某些原因A节点下线 那么redis集群将不能正常工作 因为集群找不到节点来处理0-5000的哈希槽 假如我们在创建集群之前(或者在A节点下线之前)创建了A的从节点A1 那么当A节点下线的时候 A1这个从节点会成为新的主节点 继续处理0-5000的哈希槽位 此时集群不会因为A节点的下线 导致集群无法正常使用 # 如果A1这个从节点此时也下线 那么集群仍然会失败
集群的复制特性重用了 SLAVEOF 命令的代码,所以集群节点的复制行为和SLAVEOF 命令的复制行为完全相同。
四:集群故障迁移
一:转移原理
在redis集群中,节点与节点之间会进行检查
当一个主节点下线的时候,其余主节点会检测下线的节点,并进行故障转移
换句话说,集群节点继承了线下检测以及故障转移等功能 有点类似于Sentinel的功能
因为Sentinel是一个独立的监控程序 而集群之间的下线检测以及故障转移是在节点内部完成的,其虽然功能很相似,但是运行模式并不相同,集群并没有实现Sentinel的代码
二:集群命令转发
情况一:发送到正确的节点
命令所处理的键所在的槽位正好在该节点的槽内,那么该节点就会想单机redis一样执行该命令
情况二:发送到错误的节点
接收到命令的的节点并非处理键所在槽的节点,其会发送一个转向错误(redirection),告知客户端去那个节点处理该命令,客户端会根据错误提示信息,重新向正确的节点发送命令信息
客户端根据转向错误 向正确的那个节点发送指令
五:转向错误
在集群中每个节点都会告诉其余节点自己所维护的槽
集群中每个节点都会记录0-16384这些槽位有哪些节点负责,所有节点将槽汇聚在一起,从而形成一个槽表(slot table)
节点在收到键的时候 会通过槽表查看该键位所在的槽是否在自己当前的槽范围内
1:如果在 该节点则会执行该命令
2:如果不在 则会从槽表取出正确的节点地址信息 然后发送转向错误 告诉客户端
给定 redis-trib.rb 程序的命令是 create , 这表示我们希望创建一个新的集群。
选项 --replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。
七:集群管理
Redis下载安装
下载
wget http://download.redis.io/releases/redis-3.2.4.tar.gz
安装
yum -y install gcc gcc-c++ kernel-devel # 下载依赖
tar -zxvf redis-3.2.4.tar.gz -C /usr/loacl
cd redis-3.2.4
make
make PREFIX=/usr/local/redis install
环境变量
vim /etc/profile
export PATH="$PATH:/usr/local/redis/bin"
# 保存退出
# 让环境变量立即生效
source /etc/profile
集群搭建
拷贝文件
cd /usr/local/redis-3.2.4/src/
cp redis-trib.rb /usr/local/bin/
创建集群文件
mkdir /usr/local/redis-cluster/
cd /usr/local/redis-cluster/
mkdir {7000,7001,7002,7003,7004,7005}
cp redis-3.2.4/redis.conf redis-cluster/7000 # 依次复制到上述的文件夹
修改配置文件
# 其余配置文件下同
cd /usr/local/redis-cluster/7000
vim redis.conf
84 port 7000 #端口[7000-7005]
61 bind 0.0.0.0 #IP根据节点机器可访问IP
128 daemonize yes #redis后台运行
150 pidfile /var/run/redis_7000.pid #pidfile文件对应[7000-7005]
247 dir /usr/local/redis-cluster/7000 #目录端口必须配置指定,否则无法启动
593 appendonly yes #打开持久化
721 cluster-enabled yes #开启集群模式
729 cluster-config-file nodes-7000.conf #集群的配置,配置文件首次启动自动生成
735 cluster-node-timeout 6000 #请求超时自行设置
redis-server 7000/redis.conf # 启动redis 其余的配置文件依旧
ruby
安装
yum -y install ruby rubygems && gem install redis -v 3.2.2
创建redis集群
redis-trib.rb create --replicas 1 0.0.0.0:7000 0.0.0.0:7001 0.0.0.0:7002 0.0.0.0:7003 0.0.0.0:7004 0.0.0.0:7005 # 开启集群
启动集群
redis-cli -c -h 127.0.0.1 -p 7002
CLUSTER INFO # 查看集群信息
CLUSTER NODES # 查看集群节点
创建开机自启脚本
vim /etc/init.d/redis #!/bin/sh # chkconfig: 2345 80 90 # # Simple Redis init.d script conceived to work on Linux systems # as it does use of the /proc filesystem. REDISPORT1=7000 REDISPORT2=7001 REDISPORT3=7002 REDISPORT4=7003 REDISPORT5=7004 REDISPORT6=7005 EXEC=/usr/local/redis/bin/redis-server CLIEXEC=/usr/local/redis/bin/redis-cli PIDFILE=/var/run/redis_${REDISPORT1}.pid CONF1="/usr/local/redis-cluster/${REDISPORT1}/redis.conf" CONF2="/usr/local/redis-cluster/${REDISPORT2}/redis.conf" CONF3="/usr/local/redis-cluster/${REDISPORT3}/redis.conf" CONF4="/usr/local/redis-cluster/${REDISPORT4}/redis.conf" CONF5="/usr/local/redis-cluster/${REDISPORT5}/redis.conf" CONF6="/usr/local/redis-cluster/${REDISPORT6}/redis.conf" case "$1" in start) if [ -f $PIDFILE ] then echo "$PIDFILE exists, process is already running or crashed" else echo "Starting Redis cluster server..." $EXEC $CONF1 & $EXEC $CONF2 & $EXEC $CONF3 & $EXEC $CONF4 & $EXEC $CONF5 & $EXEC $CONF6 & echo "启动成功..." fi ;; stop) if [ ! -f $PIDFILE ] then echo "$PIDFILE does not exist, process is not running" else PID=$(cat $PIDFILE) echo "Stopping ..." $CLIEXEC -p $REDISPORT1 shutdown $CLIEXEC -p $REDISPORT2 shutdown $CLIEXEC -p $REDISPORT3 shutdown $CLIEXEC -p $REDISPORT4 shutdown $CLIEXEC -p $REDISPORT5 shutdown $CLIEXEC -p $REDISPORT6 shutdown while [ -x /proc/${PID} ] do echo "Waiting for Redis cluster to shutdown ..." sleep 1 done echo "Redis cluster stopped" fi ;; *) echo "Please use start or stop as first argument" ;; esac chmod a+x /etc/init.d/redis chkconfig --add redis chkconfig redis on
写数据,查看集群状态
redis-cli -c -p 7000
set foo bar
get foo
重新分片实践
cd /usr/local/redis/src/
./redis-trib.rb reshard 127.0.0.1:7000
集群状态
redis-cli -p 7000 cluster nodes | grep master
故障转移
redis-cli -p 7002 debug segfault
查看状态
redis-cli -p 7000 cluster nodes | grep master
增加新的节点
./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000
删除一个节点
redis-trib del-node ip:port '<node-id>'
# 删除master节点之前首先要使用reshard移除master的全部slot,然后再删除当前节点
添加一个从节点
./redis-trib.rb add-node --slave --master-id $[nodeid] 127.0.0.1:7008 127.0.0.1:7000
八:Python配置Redis集群
一:安装
pip install redis-py-cluster
二:配置文件
START_NODES = [ # redis的IP和端口 {'host':'172.16.199.1','port':7000}, {'host':'172.16.199.1','port':7001}, {'host':'172.16.199.1','port':7002}, {'host':'172.16.199.1','port':7003}, {'host':'172.16.199.1','port':7004}, {'host':'172.16.199.1','port':7005} ]
三:使用方式
from rediscluster import RedisCluster # 导入集群类 from django.conf import settings # 导入配置文件中的集群节点 def test_cluster(): # 连接redis集群节点 redis_cluster = RedisCluster(startup_nodes=settings.START_NODES) redis_cluster.set('name','SR')