本文最后更新于 1 分钟前,文中所描述的信息可能已发生改变。
在生产环境中,MySQL 一般都会做主从复制和高可用切换;但如果 Redis 仍然停留在“单机 + 简单从库”的阶段,就很容易出现数据库已经切到新主库,但缓存层没有跟着切换的问题:
- 应用已经连到新的 MySQL 主库,但 Redis 依然是旧主库的从库
- 写请求失败或一直打到只读从库,造成缓存穿透、数据不一致
因此,Redis 的主从角色必须与 MySQL 的主从切换保持一致。本文记录一次在 CentOS 7 / Ubuntu 20.04 环境中,使用 Redis 6.x + Keepalived 2.x 搭建高可用、支持秒级切换的实践过程,重点总结踩坑和解决方案。
一、整体环境与目标
- 操作系统:CentOS 7 / Ubuntu 20.04
- 组件版本:Redis 6.x、Keepalived 2.x
- 目标:
- MySQL 使用 Keepalived + 检测脚本实现主从切换
- Redis 跟随 MySQL 的主从切换,实现:
- 主节点故障后,备节点自动接管 VIP
- 备机 Redis 自动提升为主库(
SLAVEOF NO ONE) - 原主恢复后,根据策略可降为从库
1.1 拓扑架构
| 节点 | IP | 角色 |
|---|---|---|
| Node A | 10.111.101.3 | MySQL 主 + Redis 主(初始) |
| Node B | 10.111.101.6 | MySQL 从 + Redis 从(初始) |
| VIP | 10.111.101.8 | 浮动 IP,由 Keepalived 统一管理 |
Keepalived 通过 MySQL 健康检查脚本(如 check_mysql.sh)判断主库是否可用,触发 VRRP 状态切换;同时通过 notify_master / notify_backup 脚本完成 Redis 主从角色切换。
二、安装 Redis 和 Keepalived
2.1 安装 Redis
Ubuntu/Debian 系统
# 更新软件包列表
sudo apt update
# 安装 Redis
sudo apt install redis-server -y
# 启动 Redis 服务
sudo systemctl start redis-server
# 设置开机自启
sudo systemctl enable redis-serverCentOS/RHEL 系统
# 安装 EPEL 仓库
sudo yum install epel-release -y
# 安装 Redis
sudo yum install redis -y
# 启动 Redis 服务
sudo systemctl start redis
# 设置开机自启
sudo systemctl enable redis配置 Redis 密码
编辑 Redis 配置文件:
# Ubuntu/Debian
sudo nano /etc/redis/redis.conf
# CentOS/RHEL
sudo nano /etc/redis.conf找到 # requirepass foobared 这一行,去掉注释并设置密码:
requirepass Hc202512保存后重启 Redis:
# Ubuntu/Debian
sudo systemctl restart redis-server
# CentOS/RHEL
sudo systemctl restart redis验证密码设置:
redis-cli -a Hc202512 ping
# 应返回 PONG2.2 安装 Keepalived
Ubuntu/Debian 系统
# 更新软件包列表
sudo apt update
# 安装 Keepalived
sudo apt install keepalived -y
# 启动 Keepalived 服务
sudo systemctl start keepalived
# 设置开机自启
sudo systemctl enable keepalivedCentOS/RHEL 系统
# 安装 Keepalived
sudo yum install keepalived -y
# 启动 Keepalived 服务
sudo systemctl start keepalived
# 设置开机自启
sudo systemctl enable keepalived2.3 验证安装
检查 Redis 版本:
redis-server --version
# 应显示 Redis 6.x 版本检查 Keepalived 版本:
keepalived --version
# 应显示 Keepalived 2.x 版本三、配置 Redis 主从和 Keepalived
3.1 清理 Redis 配置:不要写死主从关系
在所有 Redis 节点上,检查配置文件中是否存在主从相关配置:
# Ubuntu/Debian
grep -i "replicaof\|slaveof" /etc/redis/redis.conf
# CentOS/RHEL
grep -i "replicaof\|slaveof" /etc/redis.conf重要提示:
- 不要在
redis.conf中写死replicaof/slaveof - Redis 的主从关系应由 Keepalived 的通知脚本动态控制(
SLAVEOF命令),而不是依赖静态配置 - 这能避免重启后角色"自动反转"或与 Keepalived 状态不一致
3.2 编写 Redis 主从切换脚本
3.2.1 主角色脚本:/etc/keepalived/notify_redis_master.sh
当本节点成为 Keepalived 的 MASTER(拿到 VIP)时执行:
#!/bin/bash
export REDISCLI_AUTH=Hc202512
logger "[Redis-HA] $(hostname): Promoting Redis to MASTER"
# 提升为主库
redis-cli SLAVEOF NO ONE
# 主库可读写
redis-cli CONFIG SET slave-read-only no
echo "master" > /var/run/redis-role说明:
- 使用
SLAVEOF NO ONE将当前节点提升为 Redis 主节点 - 将
slave-read-only设置为no,避免残留只读限制 - 通过写入
/var/run/redis-role记录当前 Redis 角色,方便排查
3.2.2 备角色脚本:/etc/keepalived/notify_redis_backup.sh
当本节点降为 BACKUP(丢失 VIP)时执行:
#!/bin/bash
export REDISCLI_AUTH=Hc202512
PEER_IP="10.111.101.3" # 对端主节点 IP
logger "[Redis-HA] $(hostname): Demoting Redis to REPLICA of $PEER_IP"
# 设置为指定主库的从节点
redis-cli SLAVEOF "$PEER_IP" 6379
# 从库保持只读
redis-cli CONFIG SET slave-read-only yes
echo "replica" > /var/run/redis-role密码传递方式建议:
建议使用环境变量:
export REDISCLI_AUTH=Hc202512而不是:
redis-cli -a Hc202512 ...这样可以避免密码出现在 ps 输出中,更安全。
注意: 若是双主架构或 IP 会变化,需要配合额外脚本动态获取对端 IP,这里示例使用固定 IP。
3.2.3 设置脚本权限
脚本写好之后,需要确保在 Linux 下可以正常执行:
# 添加执行权限
chmod +x /etc/keepalived/notify_redis_*.sh
# 检查首行是否为 Shebang
head -n1 /etc/keepalived/notify_redis_backup.sh
# 若曾在 Windows 编辑过,需转换换行符
dos2unix /etc/keepalived/notify_redis_*.sh3.3 配置 Keepalived
在 Keepalived 的配置文件(/etc/keepalived/keepalived.conf)的 vrrp_instance 中添加通知脚本:
主节点配置(10.111.101.3):
vrrp_instance VI_1 {
state MASTER
interface eno3np2
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.111.101.8/24
}
notify_master "/etc/keepalived/notify_redis_master.sh"
notify_backup "/etc/keepalived/notify_redis_backup.sh"
}备节点配置(10.111.101.6):
vrrp_instance VI_1 {
state BACKUP
interface eno3np2
virtual_router_id 51
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.111.101.8/24
}
notify_master "/etc/keepalived/notify_redis_master.sh"
notify_backup "/etc/keepalived/notify_redis_backup.sh"
}配置说明:
- 主节点(10.111.101.3)对应的
state设为MASTER,priority为 100 - 备节点(10.111.101.6)为
BACKUP,priority为 90 - 两边都要配置
notify_master/notify_backup,脚本逻辑保持一致,避免脑裂 interface需要根据实际网卡名称修改(可通过ip addr查看)
3.4 启动服务并验证
配置修改完成后,分别在两台机器上重启相关服务:
systemctl restart keepalived redis在备机上检查 Redis 主从状态:
redis-cli -a Hc202512 info replication预期输出示例:
role:slave
master_host:10.111.101.3
master_link_status:up检查 VIP 是否在主节点:
ip addr show | grep 10.111.101.8四、实战中遇到的问题
4.1 问题一:备机 Redis 始终是 master,未连到主节点
在备机执行:
redis-cli INFO replication输出类似:
role:master
connected_slaves:0按预期,此节点应该是 Redis 从库,但实际却是 master 且没有连接任何从库。
排查后发现:Keepalived 的 notify_backup.sh 脚本根本没有生效,Redis 从未执行过 SLAVEOF,所以一直保持在 master 状态。
4.2 问题二:Keepalived 日志报 Exec format error
查看系统日志:
journalctl -u keepalived -f可以看到类似报错:
Error exec-ing command '/etc/keepalived/notify_redis_backup.sh', error 8: Exec format error这类错误通常有三个典型原因:
- 脚本缺少 Shebang(第一行没有
#!/bin/bash) - 脚本为 Windows 格式(CRLF 换行符),Linux 无法正确解释
- 脚本无执行权限(缺少
chmod +x)
结果就是:Keepalived 虽然配置了 notify_*,但执行失败,Redis 角色无法切换。
4.3 问题三:主节点 Redis 重启后竟然变成 slave
在某次维护重启后,发现原本的 Redis 主节点起来后竟然是 slave 身份:
role:slave
master_host:10.111.101.6主要有两类原因:
- Redis 配置文件中写死了
replicaof(旧版本为slaveof),导致每次启动都强制变为从库 - 上一次故障切换后,未显式执行
SLAVEOF NO ONE恢复为主,仅依赖了内存状态
这会直接破坏整个集群的主从关系,甚至造成“双主”或“无主”的混乱状态。
五、问题解决方案
5.1 问题一解决方案:确保脚本正确执行
问题现象: 备机 Redis 始终是 master,未连到主节点
排查步骤:
- 检查 Keepalived 日志:
journalctl -u keepalived -f- 检查脚本是否存在且可执行:
ls -la /etc/keepalived/notify_redis_*.sh- 手动执行脚本测试:
/etc/keepalived/notify_redis_backup.sh解决方案:
确保脚本满足以下条件:
# 1. 添加执行权限
chmod +x /etc/keepalived/notify_redis_*.sh
# 2. 检查首行是否为 Shebang
head -n1 /etc/keepalived/notify_redis_backup.sh
# 应输出:#!/bin/bash
# 3. 若曾在 Windows 编辑过,需转换换行符
dos2unix /etc/keepalived/notify_redis_*.sh
# 4. 验证脚本语法
bash -n /etc/keepalived/notify_redis_backup.sh5.2 问题二解决方案:修复 Exec format error
问题现象: Keepalived 日志报 Exec format error
原因分析:
- 脚本缺少 Shebang(第一行没有
#!/bin/bash) - 脚本为 Windows 格式(CRLF 换行符),Linux 无法正确解释
- 脚本无执行权限(缺少
chmod +x)
解决方案:
# 检查文件格式
file /etc/keepalived/notify_redis_backup.sh
# 如果显示 "CRLF" 或 "with CRLF line terminators",需要转换
# 转换换行符
dos2unix /etc/keepalived/notify_redis_*.sh
# 或者使用 sed 命令
sed -i 's/\r$//' /etc/keepalived/notify_redis_*.sh
# 确保有执行权限
chmod +x /etc/keepalived/notify_redis_*.sh
# 验证脚本可以执行
bash /etc/keepalived/notify_redis_backup.sh5.3 问题三解决方案:清理 Redis 配置中的主从关系
问题现象: 主节点 Redis 重启后竟然变成 slave
排查步骤:
检查 Redis 配置文件中是否写死了主从关系:
# Ubuntu/Debian
grep -i "replicaof\|slaveof" /etc/redis/redis.conf
# CentOS/RHEL
grep -i "replicaof\|slaveof" /etc/redis.conf解决方案:
- 编辑 Redis 配置文件,注释或删除
replicaof/slaveof行:
# Ubuntu/Debian
sudo nano /etc/redis/redis.conf
# CentOS/RHEL
sudo nano /etc/redis.conf找到类似这样的行并注释掉:
# replicaof 10.111.101.3 6379
# 或
# slaveof 10.111.101.3 6379- 重启 Redis 服务:
# Ubuntu/Debian
sudo systemctl restart redis-server
# CentOS/RHEL
sudo systemctl restart redis- 验证 Redis 角色:
redis-cli -a Hc202512 info replication | grep role重要原则:
- Redis 的主从关系必须由 Keepalived 的通知脚本动态控制
- 不要在配置文件中写死主从关系
- 每次故障切换后,确保脚本正确执行了角色切换
5.4 问题排查通用方法
当遇到 Redis 主从状态异常时,按以下顺序排查:
- 检查 Keepalived 状态:
systemctl status keepalived
ip addr show | grep 10.111.101.8- 检查 Keepalived 日志:
journalctl -u keepalived -n 50- 检查 Redis 主从状态:
redis-cli -a Hc202512 info replication- 检查脚本是否执行:
cat /var/run/redis-role
journalctl | grep "Redis-HA"- 手动测试脚本:
export REDISCLI_AUTH=Hc202512
/etc/keepalived/notify_redis_master.sh
redis-cli info replication | grep role六、故障切换演练:验证是否"真高可用"
高可用架构不是“配完就算完”,必须通过故障演练来验证。
4.1 模拟主库 MySQL 故障
在主节点(10.111.101.3)上停止 MySQL:
systemctl stop mysqld观察备机(10.111.101.6):
VIP 漂移 使用
ip addr或ip a确认10.111.101.8是否已漂移到备机Redis 自动升主
bashredis-cli -a Hc202512 info replication | grep role期望输出:
inirole:master日志记录 在系统日志中查看 Keepalived 输出:
bashjournalctl -u keepalived -f理想情况下能看到类似日志:
text[Redis-HA] node-b: Promoting Redis to MASTER
4.2 主节点恢复后的行为
当原主节点的 MySQL 和 Redis 恢复后,根据 Keepalived 的配置(是否启用“抢占”)会有不同表现:
- 若启用抢占(默认):
- VIP 会重新漂移回优先级高的节点
- 对应的
notify_*会再次触发,Redis 角色也会相应变化
- 若关闭抢占:
- 当前 MASTER 节点保持不变,原主节点恢复后继续作为备机
无论哪种策略,都建议在演练阶段多次测试,确保 MySQL 与 Redis 的角色始终一致。
七、经验总结与排查要点
可以用一个简化表格来总结本次实践中的关键点:
| 关键点 | 说明 |
|---|---|
不要在 redis.conf 写死 replicaof | 主从关系必须由脚本动态控制 |
| 脚本必须可执行 | #!/bin/bash + chmod +x + dos2unix |
| 密码传递方式要安全 | 推荐 export REDISCLI_AUTH 而非 -a |
| 日志是排查问题的关键 | journalctl -u keepalived、Redis 日志等 |
| 定期进行切换演练 | 避免"纸面高可用",确保真实故障也能扛得住 |
高可用架构的价值,在于能经得起真正的故障冲击,而不是停留在配置文件和文档上。
八、参考资料
- Redis 官方文档:
https://redis.io/docs/
- Keepalived 官网:
https://www.keepalived.org/
目前该方案在生产环境中已经稳定运行,主从切换基本可以做到秒级完成,业务无明显感知。 如果你也在搭建 Redis 高可用,欢迎参考以上实践,并结合自身环境做进一步优化和扩展。