说明:如果打算将本文内容应用于生产环境,建议对相关参数进行适当调整,以确保系统的稳定性和性能优化。
背景
长期以来,我们一直在使用Redis,但始终未能形成一个高效的运维模式来快速搭建Redis环境。因此,我编写了这份文档,旨在作为一份备忘录,同时也希望能与团队分享,以便我们能够更高效地协作和提升工作效率。
为了便于大家理解,接下来我将展示如何通过编写Shell脚本快速搭建Redis环境。如果条件允许,也可以基于这些脚本逻辑,采用Docker容器化技术来构建Redis,以实现更加灵活和便捷的部署。
一、Redis 下载安装
- 下载地址:https://download.redis.io/releases/
- 选择版本:redis-7.4.0
- 安装包名称:redis-7.4.0.tar.gz
- 详细命令
# 进入安装目录
cd /home/middleware/redis# 下载
wget https://download.redis.io/releases/redis-7.4.0.tar.gz# 解压
tar -zxvf redis-7.4.0.tar.gz# 进入redis目录
cd redis-7.4.0# 下载相关依赖
yum -y install gcc gcc-c++ tcl automake autoconf libtool make# 安装编译redis(安装后,redis默认设置到环境变量中)
make && make install
二、Redis 主从复制 搭建
- 主从复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。
- 主从复制是高可用Redis的基础,哨兵和集群都是在主从复制基础上实现高可用的。
- 缺陷:故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。
1、创建配置目录
# 创建配置目录(下面的脚本都放到该目录,具体目录可自定义)
mkdir /home/middleware/redis/redis-master-slavecd /home/middleware/redis/redis-master-slave
2、创建模板配置文件 redis.conf.template
- 创建配置文件
# 创建配置文件
vim redis.conf.template
- 模板配置文件内容如下:
- 模板配置文件中的占位符:
- custom_port 自定义端口占位符
- master_pwd 主节点密码
- replicaof_master_ip_port 从节点同步主节点占位符
# 修改监听地址
bind 0.0.0.0# 关闭保护模式,默认yes,让redis支持远程连接
protected-mode no# redis监听端口,默认6379
port custom_port# 开启守护进程,以独立进程启动,默认no
daemonize yes# 设置pidfile,默认redis.pid
pidfile redis_custom_port.pid# 日志文件,默认redis.log
logfile "redis_custom_port.log"# 禁用save命令,同步执行有性能问题,推荐使用bgsave
save ""# 设置快照文件名称,默认dump.rdb
dbfilename dump_custom_port.rdb# 设置数据目录,默认./
dir ./# 连接主节点的密码,配置在从节点,默认12345
masterauth 'master_pwd'# 连接Redis服务的redis密码,配置在主节点,默认''
requirepass 'master_pwd' # 设置最大内存,单位kb/mb/gb
maxmemory 256mb# 设置内存淘汰策略,默认noeviction不淘汰数据
# volatile-lru 按照LRU算法逐出原有数据,但仅逐出设置了过期时间的数据
maxmemory-policy volatile-lru# 开启AOF持久化,默认no
appendonly yes# AOF持久化文件名称,默认appendonly.aof
appendfilename "appendonly_custom_port.aof"# 从节点配置 -----------------------------
# 指定要同步的Master节点IP和端口
# replicaof 127.0.0.1 6379
#replicaof_master_ip_port
3、创建控制脚本 redis-master-slave.sh
- 创建脚本
# 创建脚本
vim redis-master-slave.sh# 授权
chmod 777 redis-master-slave.sh
- 脚本内容如下:
- 请根据自己需要,修改脚本中的属性
#!bin/bashPROG_NAME=$0
ACTION=$1# master ip和port
master_ip=172.18.31.17
master_port=6379
master_pwd=123456# 一台机器上启动的从节点的端口列表
slave_ports=(6380 6381)# 当前路径
curr_path=$(pwd)# redis模板配置文件
redis_conf_tempalte=redis.conf.template# redis模板配置文件中的占位符
old_char=custom_port
old_master_pwd=master_pwd# 针对从节点配置,做特殊处理
old_replicaof="#replicaof_master_ip_port"
new_replicaof="replicaof $master_ip $master_port"# 数组长度
slave_ports_len=${#slave_ports[@]}usage() {cat << EOF该脚本用于操作 Redis 主从节点.Usage: $PROG_NAME OptionsOptions:create_master_conf 批量生成主节点配置文件create_slave_conf 批量生成从节点配置文件start_master_node 批量启动主节点start_slave_node 批量启动从节点start_node 批量启动主节点和从节点stop_node 批量停止主节点和从节点restart_node 批量重启主节点和从节点
EOFexit 2
}# --------------------------------------------------------------
# 第一步:生成redis配置文件(基于模板配置文件redis.conf.template)
# 作用:无需手动修改redis配置文件中的内容,可简化操作
# --------------------------------------------------------------# 1、生成主节点redis配置文件
create_master_conf() {dest_filename=$master_port/redis-$master_port.confif [ ! -d "$master_port" ]; thenmkdir -p "$master_port"echo "Created directory: $master_port"fiif [ ! -e "$dest_filename" ]; thenecho "复制配置文件: $dest_filename"cp $redis_conf_tempalte $dest_filenameecho "替换配置文件中的占位符 $dest_filename"sed -i "s/$old_char/$master_port/g" $dest_filenamesed -i "s/$old_master_pwd/$master_pwd/g" $dest_filenameecho ""elseecho "配置文件已存在: $dest_filename"fi
}# 2、生成从节点redis配置文件
# 注意:每个从节点所在机器都需要执行一次
create_slave_conf() {for (( i=0; i<$slave_ports_len; i++ ))donew_char=${slave_ports[$i]}dest_filename=$new_char/redis-$new_char.confif [ ! -d "$new_char" ]; thenmkdir -p "$new_char"echo "Created directory: $new_char"fiif [ ! -e "$dest_filename" ]; thenecho "复制配置文件: $dest_filename"cp $redis_conf_tempalte $dest_filenameecho "替换配置文件中的占位符 $dest_filename"sed -i "s/$old_char/$new_char/g" $dest_filenamesed -i "s/$old_master_pwd/$master_pwd/g" $dest_filenamesed -i "s/$old_replicaof/$new_replicaof/g" $dest_filenameecho ""elseecho "配置文件已存在: $dest_filename"fidone
}# --------------------------------------------------------------
# 第二步:启动redis节点
# --------------------------------------------------------------# 1、启动redis主节点
start_master_node() { echo "redis-$master_port start"cd $curr_path/$master_port# 异步执行nohup redis-server redis-$master_port.conf >/dev/null 2>&1 &echo "redis-$master_port end"echo ""
}# 2、批量启动redis从节点
# 注意:每个从节点所在机器都需要执行一次
start_slave_node() {for (( i=0; i<$slave_ports_len; i++ ))donew_char=${slave_ports[$i]}echo "redis-$new_char start"cd $curr_path/$new_char# 异步执行nohup redis-server redis-$new_char.conf >/dev/null 2>&1 &echo "redis-$new_char end"echo ""done
}# --------------------------------------------------------------
# 第三步:停止redis节点
# --------------------------------------------------------------
stop_node() {# 获取redis进程id列表,将ps awk 处理的信息转换为数组pids=(`ps -ef | grep 'redis-server' | grep -v grep |grep -v 'redis-master-slave.sh'| awk '{print$2}'`)if [ ${#pids[@]} -eq 0 ]; thenecho -e "\rno redis process"returnfiecho "stop redis process"times=60for e in $(seq 60)dosleep 1cost_time=$(($times - $e ))pids=(`ps -ef | grep 'redis-server' | grep -v grep |grep -v 'redis-master-slave.sh'| awk '{print$2}'`)if [ ${#pids[@]} -eq 0 ]; thenecho -e "\rredis process has exited"break;fifor pid in ${pids[*]}dokill -9 $pidecho -e "\r -- $pid stopping redis lasts `expr $cost_time` seconds."donedoneecho ""
}case "$ACTION" increate_master_conf)create_master_conf;;create_slave_conf)create_slave_conf;;start_master_node)start_master_node;;start_slave_node)start_slave_node;;start_node)start_master_nodestart_slave_node;;stop_node)stop_node;;restart_node)stop_nodestart_master_nodestart_slave_node;;*)usage;;
esac
4、执行控制脚本
- 按顺序执行如下命令,搭建Redis 主从
# 主节点所在机器(172.18.31.17)# 1、生成主节点配置文件
sh redis-master-slave.sh create_master_conf# 2、生成从节点redis配置文件
sh redis-master-slave.sh create_slave_conf# 3、启动主从节点
sh redis-master-slave.sh start_node# 或者,分开启动主从节点
sh redis-master-slave.sh start_master_node
sh redis-master-slave.sh start_slave_node# 从节点所在机器(172.18.31.16)
# 1、生成从节点redis配置文件
sh redis-master-slave.sh create_slave_conf# 2、启动从节点
sh redis-master-slave.sh start_slave_node
5、测试命令
# 查看主从复制信息
redis-cli -h 127.0.0.1 -p 6379 -a 123456 info replication# 验证主从复制# 登录主节点
redis-cli -h 127.0.0.1 -p 6379 -a 123456keys *set name 123get name# 登录从节点
redis-cli -h 127.0.0.1 -p 6380 -a 123456keys *
三、Redis Sentinel 搭建
注意: 新版本redis自带哨兵,不需要单独安装
- 在上面主从复制模式的基础上,新增sentinel哨兵的能力。
- Redis Sentinel是Redis官方提供的高可用性解决方案,主要用于监控和管理Redis服务器,确保在主服务器发生故障时能够自动进行故障转移,从而保证服务的连续性和高可用性。
- 主要功能
- 主从监控:定期向主服务器和从服务器发送PING命令,检查它们是否在线
- 故障转移:如果master 宕机,自动根据投票数将 slave 切换为新 master
- 消息通知:哨兵可以将故障转移的结果发送给客户端
- 配置中心:客户端通过连接哨兵来获得当前Redis服务的主节点地址
- 缺陷:写操作无法负载均衡;存储能力受到单机的限制;哨兵无法对从节点进行自动故障转移,在读写分离场景下,从节点故障会导致读服务不可用,需要对从节点做额外的监控、切换操作。
1、创建配置目录
# 创建配置目录(下面的脚本都放到该目录,具体目录可自定义)
mkdir /home/middleware/redis/redis-sentinelcd /home/middleware/redis/redis-sentinel
2、创建模板配置文件 sentinel.conf.template
- 创建配置文件
# 创建配置文件
vim sentinel.conf.template
- 模板配置文件内容如下:
- 模板配置文件中的占位符:custom_port 自定义端口占位符
# 关闭保护模式,让redis支持远程连接
protected-mode no# 端口,默认26379
port custom_port# 指定sentinel为后台启动
daemonize yes# 设置pidfile,默认redis-sentinel.pid
pidfile sentinel_custom_port.pid# 日志存放路径
logfile "sentinel_custom_port.log"# 指定数据存放路径
dir ./# 配置监听主服务器
# mymaster:自定义redis主节点名称,在一个sentinel网络中,一个redis主节点只能有一个名称
# 172.19.223.161:表示主节点ip
# 6379:表示主节点port
# 2:表示至少需要2个哨兵认为主节点不可⽤时,才会进⾏failover操作
sentinel monitor mymaster master_ip master_port 2# 配置服务密码
# mymaster:redis主节点名称(与上一致);
# 123456:redis主节点和从节点的密码
sentinel auth-pass mymaster master_pwd# 判定服务器down掉的时间周期,默认30000毫秒(30秒)
sentinel down-after-milliseconds mymaster 30000# 故障转移的最大超时时间为180000(180秒)
sentinel failover-timeout mymaster 180000
3、创建控制脚本 redis-sentinel.sh
- 创建脚本
# 创建脚本
vim redis-sentinel.sh# 授权
chmod 777 redis-sentinel.sh
- 脚本内容如下:
- 请根据自己需要,修改脚本中的属性
#!bin/bashPROG_NAME=$0
ACTION=$1# master ip和port
master_ip=172.18.31.17
master_port=6379
master_pwd=123456# 一台机器上启动的sentinel的端口列表
sentinel_ports=(26379 26380 26381)# 当前路径
curr_path=$(pwd)# sentinel模板配置文件
sentinel_conf_tempalte=sentinel.conf.template# 模板配置文件中的占位符
old_char=custom_port# 主节点占位符
old_master_ip="master_ip"
old_master_port="master_port"
old_master_pwd="master_pwd"# 数组长度
sentinel_ports_len=${#sentinel_ports[@]}usage() {cat << EOF该脚本用于操作 Redis 主从节点.Usage: $PROG_NAME OptionsOptions:create_sentinel_conf 批量生成sentinel配置文件start_sentinel_node 批量启动sentinel节点stop_node 批量停止sentinel节点restart_node 批量重启sentinel节点
EOFexit 2
}# --------------------------------------------------------------
# 第一步:生成sentinel配置文件
# 注意:每个sentinel节点所在机器都需要执行一次
# --------------------------------------------------------------
create_sentinel_conf() {for (( i=0; i<$sentinel_ports_len; i++ ))donew_char=${sentinel_ports[$i]}dest_filename=$new_char/sentinel-$new_char.confif [ ! -d "$new_char" ]; thenmkdir -p "$new_char"echo "Created directory: $new_char"fiif [ ! -e "$dest_filename" ]; thenecho "复制配置文件: $dest_filename"cp $sentinel_conf_tempalte $dest_filenameecho "替换配置文件中的占位符 $dest_filename"sed -i "s/$old_char/$new_char/g" $dest_filenamesed -i "s/$old_master_ip/$master_ip/g" $dest_filenamesed -i "s/$old_master_port/$master_port/g" $dest_filenamesed -i "s/$old_master_pwd/$master_pwd/g" $dest_filenameecho ""elseecho "配置文件已存在: $dest_filename"fidone
}# --------------------------------------------------------------
# 第二步:启动sentinel节点
# 注意:每个sentinel节点所在机器都需要执行一次
# --------------------------------------------------------------
start_sentinel_node() {for (( i=0; i<$sentinel_ports_len; i++ ))donew_char=${sentinel_ports[$i]}echo "sentinel-$new_char start"cd $curr_path/$new_charnohup redis-sentinel sentinel-$new_char.conf >/dev/null 2>&1 &echo "sentinel-$new_char end"echo ""done
}# --------------------------------------------------------------
# 第三步:停止sentinel节点
# --------------------------------------------------------------
stop_node() {# 获取redis进程id列表,将ps awk 处理的信息转换为数组pids=(`ps -ef | grep 'redis-sentinel' | grep -v grep |grep -v 'redis-sentinel.sh'| awk '{print$2}'`)if [ ${#pids[@]} -eq 0 ]; thenecho -e "\rno redis process"returnfiecho "stop redis process"times=60for e in $(seq 60)dosleep 1cost_time=$(($times - $e ))pids=(`ps -ef | grep 'redis-sentinel' | grep -v grep |grep -v 'redis-sentinel.sh'| awk '{print$2}'`)if [ ${#pids[@]} -eq 0 ]; thenecho -e "\rredis process has exited"break;fifor pid in ${pids[*]}dokill -9 $pidecho -e "\r -- $pid stopping redis lasts `expr $cost_time` seconds."donedoneecho ""
}case "$ACTION" increate_sentinel_conf)create_sentinel_conf;;start_sentinel_node)start_sentinel_node;;stop_node)stop_node;;restart_node)stop_nodestart_sentinel_node;;*)usage;;
esac
4、执行控制脚本
- 按顺序执行如下命令,搭建Redis Sentinel
# 1、生成sentinel配置文件
sh redis-sentinel.sh create_sentinel_conf# 2、启动sentinel节点
sh redis-sentinel.sh start_sentinel_node
5、测试命令
# 第一步:查看:sentinel 信息(主节点为6379)
[root@centos7 redis-sentinel]# redis-cli -h 127.0.0.1 -p 26379 info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_tilt_since_seconds:-1
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=172.18.31.17:6379,slaves=2,sentinels=3# 第二步:模拟:kill 掉主节点 6379
[root@centos7 redis-master-slave]# ps -ef | grep redis
root 2518 1 0 11:23 ? 00:00:07 redis-server 0.0.0.0:6380
root 2519 1 0 11:23 ? 00:00:07 redis-server 0.0.0.0:6379
root 2520 1 0 11:23 ? 00:00:07 redis-server 0.0.0.0:6381
root 2656 1 1 11:37 ? 00:00:03 redis-sentinel *:26379 [sentinel]
root 2657 1 1 11:37 ? 00:00:03 redis-sentinel *:26380 [sentinel]
root 2658 1 1 11:37 ? 00:00:03 redis-sentinel *:26381 [sentinel]
root 2696 1952 0 11:42 pts/0 00:00:00 grep --color=auto redis[root@centos7 redis-master-slave]# kill -9 2519# 第三步:等待几秒,查看:sentinel 信息(主节点为6381,说明已自动切换主节点)
[root@centos7 redis-sentinel]# redis-cli -h 127.0.0.1 -p 26379 info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_tilt_since_seconds:-1
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=172.18.31.17:6381,slaves=2,sentinels=3# 第四步:查看主从复制信息(6381变为主节点,且只有一个6380的从节点)
[root@centos7 redis-sentinel]# redis-cli -h 127.0.0.1 -p 6381 -a 123456 info replication
# Replication
role:master
connected_slaves:1
slave0:ip=172.18.31.17,port=6380,state=online,offset=16852,lag=0
master_failover_state:no-failover
master_replid:b763222247189ca9cd4f51b69f3cca41e1b42c7e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:16852
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:16852
四、Redis Cluster 搭建【推荐】
- Redis Cluster模式解决了写操作无法负载均衡,以及单机存储限制的问题,实现了较为完善的高可用方案。
- Redis Cluster模式中集群节点最小配置6个节点(3主3从),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。
1、创建配置目录
# 创建集群配置目录(下面的脚本都放到该目录,具体目录可自定义)
mkdir -p /home/middleware/redis/redis-clustercd /home/middleware/redis/redis-cluster
2、创建模板配置文件 redis.cluster.conf.template
- 创建配置文件
# 创建配置文件
vim redis.cluster.conf.template
- 模板配置文件内容如下:
- 模板配置文件中的占位符:custom_port
- 说明:
- 若有参数需调整,可先修改该模板配置文件的内容,再执行后面的脚本。
- 重点注意 requirepass 和 maxmemory的配置。
# 注释掉 bind 项,默认监听所有网卡
# bind 127.0.0.1
bind 0.0.0.0# 关闭保护模式,默认yes,让redis支持远程连接
protected-mode no# redis监听端口,默认6379
port custom_port# 开启守护进程,以独立进程启动,默认no
daemonize yes# 设置pidfile,默认redis.pid
pidfile redis_custom_port.pid# 日志文件,默认redis.log
logfile "redis_custom_port.log"# 禁用save命令,同步执行有性能问题,推荐使用bgsave
save ""# 默认yes【设置为no解决window11启动报错EXCEPTION_ACCESS_VIOLATION的问题】
stop-writes-on-bgsave-error no# 设置快照文件名称,默认dump.rdb
dbfilename dump_custom_port.rdb# 设置数据目录,默认./
dir ./# 从节点重新规划周期,默认10
repl-ping-slave-period 10# 连接主节点的密码,配置在从节点,默认12345
masterauth '123456'# 连接Redis服务的redis密码,配置在主节点,默认''
# 集群版建议将各个节点的masterauth和requirepass设置为相同的密码,因为从节点也可能升级为主节点
requirepass '123456'# 设置最大内存,单位kb/mb/gb
maxmemory 256mb# 设置内存淘汰策略,默认noeviction不淘汰数据
# volatile-lru 按照LRU算法逐出原有数据,但仅逐出设置了过期时间的数据
maxmemory-policy allkeys-lru# 开启AOF持久化,默认no
appendonly yes# AOF持久化文件名称,默认appendonly.aof
appendfilename "appendonly_custom_port.aof"# 开启群集功能,默认no
cluster-enabled yes# 群集节点名称文件设置,默认nodes-6379.conf
cluster-config-file nodes_custom_port.conf# 设置群集节点超时时间,默认15000
# 集群中每个节点都会定期向其他节点发送ping消息,接收节点回复pong消息作为响应。
# 如果在cluster-node-timeout时间内通信一直失败,则发送节点会认为接收节点存在故障,把接收节点标记为主观下线(pfail)状态。默认15000,即15s。
cluster-node-timeout 15000# 从节点有效因子,提高集群故障转移能力,默认10
# 每个从节点都要检查最后与主节点断线时间,判断其是否有资格替换故障的主节点。
# 如果从节点与主节点断线时间超过 (cluster-node-timeout * cluster-slave-validity-factor) + repl-ping-slave-period,则当前从节点不具备故障转移资格。
# 例如,如果节点超时时间为30秒,从节点有效因子为10,从节点重新规划周期为10秒,如果从节点与主节点断线时间超过310秒,则当前从节点不会尝试故障转移。
cluster-slave-validity-factor 10# 迁移屏障,提高集群抵抗故障的能力,默认1
# 迁移屏障为1,表示只有当主节点至少保留一个从节点时,从节点才会迁移
# 要禁用迁移,只需将其设置为一个非常大的值。
cluster-migration-barrier 1# 设置集群可用性,默认为yes
# yes 表示所有slot都正常工作,才能对外提供服务
# no 表示部分slot出现问题,其他正常的slot仍然可以继续提供服务
cluster-require-full-coverage yes
3、创建控制脚本 redis-cluster.sh
- 创建脚本
# 创建脚本
vim redis-cluster.sh# 授权
chmod 777 redis-cluster.sh
- 脚本内容如下:
- 请根据自己的需要,修改脚本中的属性:hosts、ports、redis_pwd
#!bin/bashPROG_NAME=$0
ACTION=$1# 单机集群配置
# 举例:3主3从,1个ip,6个port,即一台机器6个节点
# hosts=("127.0.0.1")
# ports=(6001 6002 6003 6004 6005 6006)# 多机集群配置
# 举例:3主3从,3个ip,2个port,即一台机器2个节点,3主3
hosts=("172.18.31.15" "172.18.31.16" "172.18.31.17")
ports=(6001 6002)# redis密码,对应模板配置文件redis.cluster.conf.template中的密码
redis_pwd=123456# 数组长度
host_len=${#hosts[@]}
port_len=${#ports[@]}# 当前路径
curr_path=$(pwd)# redis模板配置文件
redis_conf_tempalte=redis.cluster.conf.template# redis模板配置文件中的占位符
old_char=custom_portusage() {cat << EOF该脚本用于操作 Redis Cluster.Usage: $PROG_NAME OptionsOptions:create_conf 批量生成redis配置文件start_node 批量启动redis集群节点stop_node 批量停止redis集群节点restart_node 批量重启redis集群节点create_cluster 创建redis集群
EOFexit 2
}# --------------------------------------------------------------
# 第一步:批量生成redis配置文件(基于模板配置文件redis.cluster.conf.template)
# 注意:每台机器都需要执行
# 作用:无需手动修改redis配置文件中的内容,可简化操作
# --------------------------------------------------------------
create_conf() {for (( i=0; i<$port_len; i++ ))donew_char=${ports[$i]}dest_filename=$new_char/redis-$new_char.confif [ ! -d "$new_char" ]; thenmkdir -p "$new_char"echo "Created directory: $new_char"fiif [ ! -e "$dest_filename" ]; thenecho "复制配置文件: $dest_filename"cp $redis_conf_tempalte $dest_filenameecho "替换配置文件中的占位符 $dest_filename"sed -i "s/$old_char/$new_char/g" $dest_filenameecho ""elseecho "配置文件已存在: $dest_filename"fidone
}# --------------------------------------------------------------
# 第二步:批量启动redis集群节点
# 注意:每台机器都需要执行
# --------------------------------------------------------------
start_node() {for (( i=0; i<$port_len; i++ ))donew_char=${ports[$i]}echo "redis-$new_char start"cd $curr_path/$new_char# 异步执行nohup redis-server redis-$new_char.conf >/dev/null 2>&1 &echo "redis-$new_char end"echo ""done
}# 批量停止redis集群节点
stop_node() {# 获取redis进程id列表,将ps awk 处理的信息转换为数组pids=(`ps -ef | grep 'redis-server' | grep -v grep |grep -v 'redis-cluster.sh'| awk '{print$2}'`)if [ ${#pids[@]} -eq 0 ]; thenecho -e "\rno redis process"returnfiecho "stop redis process"times=60for e in $(seq 60)dosleep 1cost_time=$(($times - $e ))pids=(`ps -ef | grep 'redis-server' | grep -v grep |grep -v 'redis-cluster.sh'| awk '{print$2}'`)if [ ${#pids[@]} -eq 0 ]; thenecho -e "\rredis process has exited"break;fifor pid in ${pids[*]}dokill -9 $pidecho -e "\r -- $pid stopping redis lasts `expr $cost_time` seconds."donedoneecho ""
}# --------------------------------------------------------------
# 第三步:创建redis集群
# 注意:在集群中任意一台机器上执行,且执行一次即可
# --------------------------------------------------------------
create_cluster() {# 生成 ip:port 格式串ip_port_list=""for (( i=0; i<$host_len; i++ ))dofor (( j=0; j<$port_len; j++ ))doip_port_list+=" ${hosts[$i]}:${ports[$j]}"donedoneecho "节点列表: $ip_port_list"# 执行创建集群命令# --cluster create 表示创建集群# --replicas 1 表示每个主节点有1个从节点,这里随机分配主从关系。如果需要定制,则可以不加该参数,使用add-node来定制。echo yes | redis-cli --cluster create $ip_port_list --cluster-replicas 1 -a $redis_pwd
}case "$ACTION" increate_conf)create_conf;;start_node)start_node;;stop_node)stop_node;;restart_node)stop_nodestart_node;;create_cluster)create_cluster;;*)usage;;
esac
4、执行控制脚本
- 按顺序执行如下命令,搭建Redis Cluster
# 第一步:基于redis模板配置文件,一键批量生成redis集群中的节点配置文件
# 注意:每台机器都需要执行
sh redis-cluster.sh create_conf# 第二步:批量启动redis集群节点
# 注意:每台机器都需要执行
sh redis-cluster.sh start_node# 注意:集群所有节点都启动后,再执行下面步骤# 第三步:创建redis集群
# 注意:在集群中任意一台机器上执行即可
sh redis-cluster.sh create_cluster# 批量停止redis集群节点
# 注意:每台机器都需要执行
sh redis-cluster.sh stop_node# 批量重启redis集群节点
# 注意:每台机器都需要执行
sh redis-cluster.sh restart_node
5、测试命令
# 测试集群# -c 表示-c(cluster),连接集群时使用,可防止moved和ask异常
# -a 表示-a(auth),redis密码,无需手动auth命令
redis-cli -h 127.0.0.1 -p 6001 -c -a 123456# 查看节点的哈希槽编号范围
cluster slots# 设置name键的值
set name abcd
-> Redirected to slot [5798] located at 127.0.0.1:6002【说明:重定向到了6002】# 查看name键的槽编号(6002)
cluster keyslot name# 查看节点6002上name建是否存在(存在)
redis-cli -h 127.0.0.1 -p 6002 -c -a 123456
keys *# 查看节点6003上name建是否存在(不存在)
redis-cli -h 127.0.0.1 -p 6003 -c -a 123456
keys *# 在节点6003上获取name键的值
get name
-> Redirected to slot [5798] located at 127.0.0.1:6002【说明:重定向到了6002】
"abcd"# 手动关闭节点
redis-cli -h 127.0.0.1 -p 6001 -a 123456 shutdown# 查看redis节点信息
redis-cli -h 127.0.0.1 -p 6001 -a 123456 info# 每1秒打印一次info的过滤信息
# -r 表示将命令循环多少次
# -i 表示每隔几秒执行一次命令
redis-cli -h 127.0.0.1 -p 6001 -a 123456 -r 10 -i 1 info | grep used_memory_human
6、集群操作
查看集群命令帮助说明
#
redis-cli --cluster help
查看集群节点id nodes
# 查看集群中的节点id
redis-cli -h 127.0.0.1 -p 6001 -a 123456 cluster nodes# 获取slot分配
redis-cli -c -p 127.0.0.1 -p 6001 -a 123456 cluster slots# 查看主从复制信息
redis-cli -h 127.0.0.1 -p 7003 -a 123456 -c info replication
查看集群 info
# 查看集群
redis-cli --cluster info 127.0.0.1:6001 -a 123456
检查集群 check
# 检查集群
redis-cli --cluster check 127.0.0.1:6001 -a 123456# 检查集群中是否有槽同时被分配给了多个节点,只有当所有的槽位正常时,集群状态才算OK
redis-cli --cluster check 127.0.0.1:6001 --cluster-search-multiple-owners -a 123456
迁移槽位 reshard
# 命令解释
# 迁移一个或者多个节点上的槽位至一个目标节点上
# --cluster-from <arg> #槽位来源节点id,多个用,分割,all表示全部节点,值不为all的情况下,不能包含--cluster-to
# --cluster-to <arg> #槽位目标节点id,只允一个
# --cluster-slots <arg> #迁移的槽位数
# --cluster-yes #是否默认同意集群内部的迁移计划(默认同意就可以)
# --cluster-timeout <arg> #迁移命令(migrate)的超时时间
# --cluster-pipeline <arg> #迁移key时,一次取出的key数量,默认10
# --cluster-replace #是否直接replace到目标节点# 迁移槽位,迁移100个槽位,将100个槽位从6001,迁移到节点6002
redis-cli --cluster reshard 127.0.0.1:6001 --cluster-from 7e482eb54f131ef01f5050e600f12d1980ea7f1e --cluster-to 9db0701a7d10a3044763b7a17365933eeab47d08 --cluster-slots 100 --cluster-yes -a 123456
平衡集群 rebalance
# 命令解释
# rebalance host:port
# --cluster-weight <node1=w1...nodeN=wN> # 槽位权重(浮点型)比值,例如(这里使用端口号代替运行id): 7001=1 7002=1 7003=2 则表示,总权重为4, 7001和7002将分配到 (16384/4)*1 个槽位,7003则是(16384/4)*2个槽位。
# --cluster-use-empty-masters
# --cluster-timeout <arg> # 迁移命令(migrate)的超时时间
# --cluster-simulate # 模拟rebalance操作,不会真正执行迁移操作
# --cluster-pipeline <arg> # 定义 getkeysinslot命令一次取出的key数量,默认值为10
# --cluster-threshold <arg> # 平衡触发的阈值条件,默认为2.00%。例如上一步,7001和7002应该有4096个槽位,如果7001的槽位数不够4096个,且超过 (4096*0.02 约等于)82个及以上;或者7001的槽位数比4096少于82个及以上,则会触发自平衡。
# --cluster-replace # 是否直接replace到目标节点# 重新分配集群各个节点负责的槽位,让各个节点负责的槽数量重新回到平均状态
redis-cli --cluster rebalance 127.0.0.1:6001 -a 123456# 平均分配所有的槽位:不手动指定槽位分配,自动将16384个槽位,分配给集群的每一个master
redis-cli --cluster rebalance 127.0.0.1:6001 --cluster-use-empty-masters --cluster-threshold 1 -a 123456# 按权重分配槽位:把node3上的槽位,平均分配到其他主节点上去
# 各个节点的权重都为1时,表示平均分配所有的槽位
redis-cli --cluster rebalance 127.0.0.1:6001 --cluster-weight node1=1 node2=1 node3=0 -a 123456# 按权重分配槽位:总权重为4,node1权重=1 node2权重=1 node3权重=2
redis-cli --cluster rebalance 127.0.0.1:6001 --cluster-weight 7e482eb54f131ef01f5050e600f12d1980ea7f1e=1 9db0701a7d10a3044763b7a17365933eeab47d08=1 7879e1312ab7d13267357bceb58f99a9ce15cd9e=2 -a 123456
集群扩容 add-node
- 扩容,即向集群中添加主节点或从节点
- 扩容后,迁移槽位时,如何保证不影响 redis 的使用?(待研究)
- 在不影响集群对外服务的情况下,为集群添加节点进行扩容,也可以下线部分节点进行缩容
- 扩容步骤:
- 第一步:添加节点
- 第二步:迁移槽位 or 槽位平衡
# 命令解释
# add-node
# new_host:new_port #新加入集群的ip和port
# existing_host:existing_port #集群中任一节点的ip和port
# --cluster-slave #新节点作为从节点,默认随机一个主节点
# --cluster-master-id <arg> #给新节点指定主节点,值为节点的运行id# 添加主节点
# 不传入--cluster-slave --cluster-master-id 参数,表示添加一个主节点。
# 注意,当添加的是一个主节点时,此时,该主节点没有任何槽位,可以使用rebalance或者reshard来迁移槽位给它。
redis-cli --cluster add-node 127.0.0.1:6007 127.0.0.1:6001 -a 123456# 槽位平衡:自动将16384个槽位,分配给集群的每一个master,不用手动指定槽位分配
redis-cli --cluster rebalance 127.0.0.1:6001 --cluster-use-empty-masters --cluster-threshold 1 -a 123456# 添加从节点(方式1)
# 假设现在集群中有6001 6002 6003 6007四个主节点,想为6007添加一个从节点127.0.0.1:6008,命令如下:
redis-cli --cluster add-node 127.0.0.1:6008 127.0.0.1:6001 --cluster-slave --cluster-master-id 0115ad0b7bbb3b95ff8e94388c14cf9189b8d2fe -a 123456# 添加从节点(方式2),随机跟一个主节点
# 注意,如果没有指定主节点,那么会去一个副本数更少的主节点做副本;如果副本数都一样,那么就随机跟一个主节点;
redis-cli --cluster add-node 127.0.0.1:6008 127.0.0.1:6001 --cluster-slave -a 123456
说明:redis集群扩容,槽位迁移时,可能会遇到超时问题,解决方案具体见下文的常见错误-问题4
集群缩容 del-node
- 缩容步骤:
- 第一步:先迁移槽位 or 槽位平衡
- 第二步:删除节点
# 命令解释
# del-node host:port node_id #删除给定的一个节点,成功后关闭该节点服务
# 用于从集群中删除指定的节点。host:port是执行命令的节点地址和端口,node_id是要删除的节点ID。# 删除从节点
redis-cli --cluster del-node 127.0.0.1:6008 98da58b16dc8becd4a1474e772ae4e24b44f0e1e -a 123456# 按权重分配槽位:把node4上的槽位,平均分配到其他主节点上去
# 注意,如果把主节点的 slot 都迁移走了,那么该主节点下的从库会自动转移到其他有 slot 的主节点去
redis-cli --cluster rebalance 127.0.0.1:6001 --cluster-weight c2fce950a3fb1c2243009f0ae34b9165f2c0242d=1 f413bd23d52255b292119d144fe0e44093044cd0=1 9470d0c3955a334efdcfa8ba79103f4b7593c8c9=1 c3f2a562b827d96e39bc285d9c33269c4e2e0d1f=0 -a 123456 # 删除主节点
redis-cli --cluster del-node 127.0.0.1:6007 07f3c418b8796197621fe9e9ea839a2c0c1289b3 -a 123456
修复集群 fix
# 修复集群 # --cluster-search-multiple-owners:是否修复多个拥有者的槽位。当集群中的槽位在迁移过程中,出现意外时,使用fix可修复该槽位。redis-cli --cluster fix 127.0.0.1:6001 --cluster-search-multiple-owners -a 123456redis-cli --cluster fix 127.0.0.1:5001 -a 123456# --cluster-fix-with-unreachable-masters:是否修复不可达的主节点上的槽位。例如,集群中某个主节点坏掉了,也没有故障转移成功。此时如何恢复该主节点上的所有槽位呢?这时就可以使用该参数,会将处于该主节点上的所有槽位恢复到存活的主节点上(之前的数据会丢失,仅仅是恢复了槽位)。redis-cli --cluster fix 127.0.0.1:6001 --cluster-fix-with-unreachable-masters -a 123456
集群上执行命令 call
# 命令解释
# call host:port command arg arg .. arg #在集群的所有节点执行相关命令
# --cluster-only-masters #是否只在主节点上执行
# --cluster-only-replicas #是否只在从节点上执行redis-cli --cluster call 127.0.0.1:6001 keys * -a 123456
redis-cli --cluster call 127.0.0.1:6001 set a aaa -a 123456
redis-cli --cluster call 127.0.0.1:6001 set b bbb -a 123456
redis-cli --cluster call 127.0.0.1:6001 set c ccc -a 123456
redis-cli --cluster call 127.0.0.1:6001 set d ddd -a 123456
集群节点超时时间设置 set-timeout
- 不建议在线去设置,有遇到过在线设置,设置成功但导致主节点挂掉的情况
# 命令解释
# set-timeout host:port milliseconds #设置整个集群的cluster-node-timeout时间redis-cli --cluster set-timeout 127.0.0.1:6001 15000 -a 123456
导入数据至集群 import
- 实际项目上,数据导入应该用 redis-shake 工具的比较多。
# 命令解释
# import host:port
# --cluster-from host:port # 来源redis node,不能为cluster node
# --cluster-from-user <arg> #
# --cluster-from-pass <arg> # 来源redis node的密码
# --cluster-from-askpass
# --cluster-copy #migrate时指定类型为copy
# --cluster-replace #migrate时指定类型为replace# 说明:外部Redis实例(127.0.0.2:6379)导入到集群中的任意一节点,导入之后,原来集群的key变为空,导入到新集群的key会自动分片到各个master节点的slot
# --cluster-replace 如果集群(127.0.01:6379)中存在外部redis实例(127.0.0.2:6379)的key,则会覆盖掉(10.35.2.68:6379)的value
# --cluster-copy 默认情况下,import 命令在向集群导入数据的同时,还会删除单机服务器中源数据。如果用户想要保留单机服务器中的数据,那么可以在执行命令的同时给定 –cluster-copy 选项 该命令将正在运行的实例的所有键(从源实例中删除键)移动到指定的预先存在的 Redis 集群。redis-cli --cluster import 127.0.0.1:6001 --cluster-from 127.0.0.1:6379 --cluster-replace --cluster-copy -a 123456
备份集群rdb文件 backup
# 命令解释
# backup host:port backup_directory #备份主节点上的数据RDB文件mkdir backup
redis-cli --cluster backup 127.0.0.1:6001 ./backup -a 123456
五、常见错误
问题1:redis集群扩容,槽位迁移时,出现超时问题,导致只迁移完成部分
- redis版本:redis-7.4.0
- 执行命令:redis-cli --cluster rebalance 127.0.0.1:6001 --cluster-use-empty-masters --cluster-threshold 1 -a 123456
- 错误信息:clusterManagerMoveSlot: IOERR error or timeout writing to target instance
- 原因分析:分析的步骤如下
# 添加主节点(成功)
[root@centos7 redis-cluster]#redis-cli --cluster add-node 127.0.0.1:6001 172.18.31.17:5001 -a 123456
省略部分日志信息... ...
>>> Getting functions from cluster
>>> Send FUNCTION LIST to 127.0.0.1:6001 to verify there is no functions in it
>>> Send FUNCTION RESTORE to 127.0.0.1:6001
>>> Send CLUSTER MEET to node 127.0.0.1:6001 to make it join the cluster.
[OK] New node added correctly.# 第一次,执行 rebalance 槽位迁移(提示迁移超时)
[root@centos7 redis-cluster]#redis-cli --cluster rebalance 127.0.0.1:6001 --cluster-use-empty-masters --cluster-threshold 1 -a 123456
省略部分日志信息... ...
[OK] All 16384 slots covered.
>>> Rebalancing across 4 nodes. Total weight = 4.00
Moving 1366 slots from 172.18.31.17:5001 to 127.0.0.1:6001
########################################################################################################################################################################################
Node 172.18.31.17:5001 replied with error:
IOERR error or timeout writing to target instance
*** clusterManagerMoveSlot: IOERR error or timeout writing to target instance# 第二次,执行 rebalance 槽位迁移(提示5798这个slot处于migrating或importing状态,因此需要修复该slot)
[root@centos7 redis-cluster]#redis-cli --cluster rebalance 127.0.0.1:6001 --cluster-use-empty-masters --cluster-threshold 1 -a 123456
省略部分日志信息... ...
[WARNING] Node 127.0.0.1:6001 has slots in importing state 5798.
[WARNING] Node 172.18.31.17:5001 has slots in migrating state 5798.
[WARNING] The following slots are open: 5798.
>>> Check slots coverage...
[OK] All 16384 slots covered.
*** Please fix your cluster problems before rebalancing# 按照提示,在rebalance前,先执行fix尝试修复(任然报如下错误)
[root@centos7 redis-cluster]#redis-cli --cluster fix 127.0.0.1:6001 -a 123456
省略部分日志信息... ...
>>> Check for open slots...
[WARNING] Node 127.0.0.1:6001 has slots in importing state 5798.
[WARNING] Node 172.18.31.17:5001 has slots in migrating state 5798.
[WARNING] The following slots are open: 5798.
>>> Fixing open slot 5798
Set as migrating in: 172.18.31.17:5001
Set as importing in: 127.0.0.1:6001
>>> Case 1: Moving slot 5798 from 172.18.31.17:5001 to 127.0.0.1:6001
Moving slot 5798 from 172.18.31.17:5001 to 127.0.0.1:6001:
Node 172.18.31.17:5001 replied with error:
IOERR error or timeout writing to target instance
- 解决方案:
# 登录相应节点
redis-cli -h 127.0.0.1 -p 6001 -c -a 123456# 修复有问题的slot
cluster setslot 5798 stable# 再次执行fix修复操作(可能不需要该步骤,因无法复现迁移超时的问题,暂时先放在这,不影响该问题的解决)
redis-cli --cluster fix 127.0.0.1:5001 -a 123456# 再次执行槽位迁移(成功)
redis-cli --cluster rebalance 127.0.0.1:5001 -a 123456
参考资料
Redis数据库——主从、哨兵、群集
Redis集群的维护(redis-cli --cluster 命令用法)
Redis6集群安装与运维管理【重要】
全面剖析Redis Cluster原理和应用
Redis官方文档
Redis-3.2.0集群配置(含常见错误)【重要】