Redis Sentinel 生产环境备份与恢复 SOP 脚本
·
Redis Sentinel 生产环境备份与恢复 SOP 操作流程
文档版本:V4.2
适用环境:Redis Sentinel 哨兵模式生产环境
最后更新:2026-02-06
1. 目的
本标准操作流程(SOP)规定了 Redis Sentinel 哨兵模式生产环境下的数据备份、配置备份、恢复及故障处理的标准操作方法,确保 Redis 集群数据的安全性、完整性和可恢复性,并规范告警通知流程。
2. 核心设计理念
本 SOP 基于以下核心原则设计:
- 数据备份:采用 RDB(从节点) + AOF(主节点) 混合备份策略,兼顾备份效率与数据完整性。
- 配置备份:同时备份 Redis 配置文件和 Sentinel 配置文件(含逻辑状态快照)。
- 自动化告警:模块化支持邮件、飞书、钉钉、企微等多种告警通道,确保运维人员及时感知备份状态。
- 脚本化操作:提供统一的 Bash 脚本
redis_sentinel_sop.sh,封装所有操作,降低人为失误。
3. 角色与职责
| 角色 | 职责 |
|---|---|
| 运维工程师 | 执行备份与恢复操作,配置告警,维护脚本,监控备份日志,处理故障。 |
| DBA | 审核备份策略,优化备份性能,制定数据保留策略,处理复杂恢复场景。 |
4. 前置条件与依赖
4.1 环境要求
- 操作系统:CentOS 7.x / Ubuntu 20.04+
- Redis 版本:>= 6.0
- 哨兵模式:已配置并运行正常
- 磁盘空间:备份目录预留足够空间(建议至少为当前数据集的 3 倍)
4.2 软件依赖
redis-cli,redis-server,redis-check-rdb,redis-check-aof(Redis 工具集)mailx(邮件告警,可选)curl(Webhook 告警)gzip,gunzip(压缩工具)scp,ssh(远程拷贝,需配置 SSH 免密登录)
4.3 权限要求
- 执行脚本的用户需对 Redis 数据目录、备份目录有读写权限。
- 执行恢复操作的用户需有
systemctl管理 Redis 服务的权限。 - SSH 用户需有远程目标主机的免密登录权限。
4.4 配置文件路径
- Redis 配置文件:
/datas/redis/conf/redis.conf - Sentinel 配置文件:
/datas/redis/conf/sentinel.conf - Redis 数据目录:
/datas/redis/data - 备份根目录:
/datas/backup/redis
5. 核心概念说明
5.1 备份策略详解
| 备份类型 | 备份对象 | 备份源 | 备份方式 | 优势 | 局限性 |
|---|---|---|---|---|---|
| RDB | 数据集快照 | 最优从节点 | 通过 redis-cli --rdb 从远程拉取 |
文件小,恢复快,对主库性能影响小 | 可能丢失最后一次快照后的数据 |
| AOF | 写命令日志 | 主节点 | 通过 BGREWRITEAOF 触发后,通过 SSH/本地拷贝 |
数据完整性高,通常只丢失1秒数据 | 文件大,恢复慢,重写时消耗资源 |
| Redis配置 | redis.conf |
本机 + 远程主节点 | 直接文件拷贝 (cp/scp) |
保存运行参数、持久化策略、安全设置 | 无 |
| Sentinel配置 | sentinel.conf + 逻辑状态 |
本机 | 物理文件拷贝 + 逻辑状态导出 | 保存哨兵拓扑、监控主库信息、投票规则 | 逻辑状态快照仅用于审计排障 |
5.2 告警通道说明
- 邮件 (
mail):正式通知,适合记录存档。 - 飞书/钉钉/企微 (
webhook):即时告警,适合快速响应。需在脚本中预先配置正确的 Webhook 地址和关键词。
6. 操作流程
6.1 备份操作
6.1.1 定期全量备份(推荐)
目的:执行完整的数据和配置备份。
频率:每日一次(建议在业务低峰期,如凌晨 2:00)
操作命令:
/root/redis_sentinel_sop.sh backup all
预期结果:
- 脚本将依次执行 RDB、AOF、Redis 配置、Sentinel 配置的备份。
- 所有备份文件会被压缩 (
.gz) 并存放在相应的子目录下。 - 旧备份文件会根据保留策略自动清理(RDB/AOF: 7天, Redis 配置: 30天, Sentinel 配置: 30天)。
- 成功或失败会触发告警通知。
备份文件命名规范:
# RDB (备份最优从节点)
redis_rdb_{slave_ip}_{timestamp}.rdb.gz
# AOF (备份主节点)
appendonly_{master_ip}_{timestamp}.aof.gz
# Redis 配置 (本地)
redis_local_{timestamp}.conf.gz
# Redis 配置 (远程主节点)
redis_master_{master_ip}_{timestamp}.conf.gz
# Sentinel 配置 (物理文件)
sentinel_conf_{timestamp}.conf.gz
# Sentinel 状态 (逻辑快照)
sentinel_state_{timestamp}.conf.gz
6.1.2 单独备份
目的:针对特定类型进行备份。
场景:需要快速备份某个特定组件,或作为排查问题的补充手段。
操作命令:
# 仅备份 RDB
/root/redis_sentinel_sop.sh backup rdb
# 仅备份 AOF
/root/redis_sentinel_sop.sh backup aof
# 仅备份 Redis 配置
/root/redis_sentinel_sop.sh backup redis_config
# 仅备份 Sentinel 配置 (V4.2 新增,包含逻辑状态)
/root/redis_sentinel_sop.sh backup sentinel_config
# 备份所有配置 (Redis + Sentinel)
/root/redis_sentinel_sop.sh backup config
6.2 恢复操作
⚠️ 警告:恢复操作会覆盖现有数据,是一个高风险操作。执行前必须:
- 确认恢复目标环境正确(切勿在生产环境操作)。
- 确认要恢复的备份文件无误。
- 通知所有相关人员,获得明确批准。
- 在执行恢复操作前,务必对当前数据进行再次备份。
- 建议在测试环境验证恢复流程后,再在生产环境执行。
6.2.1 RDB 恢复
目的:从 RDB 快照恢复数据。
场景:数据误删除、需要将数据回滚到某个时间点、搭建从库。
操作步骤:
- 停止服务:
systemctl stop redis - 备份当前文件(关键安全步骤):
mv /datas/redis/data/dump.rdb /datas/redis/data/dump.rdb.bak_$(date +%s) - 执行恢复脚本:
脚本会自动解压、校验、覆盖文件。/root/redis_sentinel_sop.sh restore rdb /path/to/your/backup/redis_rdb_xxx.rdb.gz - 启动服务并验证:
systemctl start redis # 等待几秒后检查 redis-cli INFO server redis-cli DBSIZE
6.2.2 AOF 恢复
目的:从 AOF 日志恢复数据。
场景:RDB 文件损坏、需要尽可能少地丢失数据。
操作步骤:
- 停止服务:
systemctl stop redis - 备份当前文件(关键安全步骤):
mv /datas/redis/data/appendonly.aof /datas/redis/data/appendonly.aof.bak_$(date +%s) - 执行恢复脚本:
脚本会自动解压、校验、覆盖文件。/root/redis_sentinel_sop.sh restore aof /path/to/your/backup/appendonly_xxx.aof.gz - 启动服务并验证:
systemctl start redis # 等待 Redis 加载 AOF 并可能进行 AOF 重写 redis-cli INFO server redis-cli DBSIZE
6.3 故障处理流程
6.3.1 备份失败告警
现象:收到“Redis Backup Failed”或“Checksum Failed”告警。
排查步骤:
- 查看日志:
查找具体错误信息(如 SSH 连接失败、Redis 命令执行失败、磁盘空间不足等)。tail -n 50 /var/log/redis_sentinel_sop.log - 手动验证:
- 尝试手动执行失败的备份命令(如手动
redis-cli --rdb)。 - 检查 Redis 哨兵状态是否正常 (
redis-cli -p 26379 SENTINEL MASTERS)。 - 检查网络连通性和 SSH 免密登录。
- 尝试手动执行失败的备份命令(如手动
- 恢复服务:
- 修复问题后,立即执行一次手动全量备份。
- 如果是存储空间问题,及时清理旧备份或扩容。
6.3.2 恢复失败
现象:执行恢复操作后,Redis 服务无法启动或数据异常。
应急回滚步骤:
- 立即停止服务:
systemctl stop redis - 回滚文件:使用步骤 2 中备份的
.bak_文件恢复。cp /datas/redis/data/dump.rdb.bak_<timestamp> /datas/redis/data/dump.rdb # 或 cp /datas/redis/data/appendonly.aof.bak_<timestamp> /datas/redis/data/appendonly.aof - 启动服务:
systemctl start redis - 深入排查:在测试环境分析备份文件为何无法恢复,必要时联系 Redis 技术支持。
7. 定期维护任务
- 每周:检查备份日志,确认备份任务是否成功执行。检查备份目录磁盘使用率。
- 每月:从备份文件中抽取一份,在测试环境进行恢复演练,验证备份数据的可用性。
- 每季度:审查并优化备份保留策略。审核告警联系人列表是否有效。
8. 应急预案
8.1 主节点数据丢失且 RDB/AOF 均不可用
场景:极端灾难场景,主节点磁盘物理损坏,且无可用备份。
应对:
- 尝试从从节点抢救:选择一个数据最全的从节点,将其提升为新的主节点(可通过 Sentinel 自动故障转移或手动
SLAVEOF NO ONE)。 - 数据损失评估:评估从节点与原主节点的数据差异,告知业务方可能的数据丢失范围。
- 重建集群:在新的主节点上重新配置其他从节点。
8.2 备份存储故障
场景:备份目录所在的磁盘故障,所有备份文件丢失。
应对:
- 立即隔离故障磁盘,防止数据进一步损坏。
- 尝试数据恢复:使用专业数据恢复服务尝试从故障磁盘恢复数据。
- 重新部署备份:在新的磁盘上重新部署备份策略,并立即执行全量备份。
- 加强监控:增加对备份目录磁盘健康度(如 SMART 信息)的监控。
9. 附录
9.1 脚本快速参考
# 备份操作
/root/redis_sentinel_sop.sh backup all # 全量备份
/root/redis_sentinel_sop.sh backup rdb # RDB 备份
/root/redis_sentinel_sop.sh backup aof # AOF 备份
/root/redis_sentinel_sop.sh backup config # 配置备份
# 恢复操作 (⚠️ 高风险)
/root/redis_sentinel_sop.sh restore rdb <file> # RDB 恢复
/root/redis_sentinel_sop.sh restore aof <file> # AOF 恢复
9.2 关键配置参数说明
| 参数 | 说明 | 建议值 |
|---|---|---|
BACKUP_ROOT_DIR |
备份根目录 | /datas/backup/redis |
LOG_FILE |
脚本日志路径 | /var/log/redis_sentinel_sop.log |
REDIS_DATA_DIR |
Redis 数据目录 | /datas/redis/data |
SENTINEL_HOST/PORT |
哨兵连接信息 | 127.0.0.1 / 26379 |
MASTER_NAME |
哨兵监控的主名称 | mymaster |
REDIS_PASS |
Redis 密码 | (设置强密码) |
ALERT_XXX_ENABLED |
告警开关 | true/false |
9.3 常见问题 (FAQ)
Q1: 备份脚本执行很慢,如何优化?
A: 检查以下几点:
- 网络带宽:主从节点间、本地与备份存储间的带宽是否充足。
- 磁盘 I/O:Redis 数据目录和备份目录的磁盘 IOPS 是否饱和。
- 从节点负载:备份 RDB 的从节点是否本身负载过高。
- 优化建议:将备份目录放在独立的物理磁盘上;考虑使用
redis-cli --rdb的--replica选项(从从节点备份时效果更好)。
Q2: 收到 “Checksum Failed” 告警怎么办?
A: 这表示备份文件校验失败,文件可能损坏。立即执行:
- 检查存储介质是否健康。
- 重新执行备份操作。
- 如果连续失败,可能需要排查网络传输问题或源 Redis 实例本身的数据问题。
Q3: 如何修改备份保留时间?
A: 编辑脚本redis_sentinel_sop.sh,找到find ... -mtime +N ...命令,修改+N的值(N 为天数)。修改后重启定时任务(如果使用 cron)或下次执行时生效。
Q4: 恢复后,数据量和备份前不一致?
A: 这通常是正常的:
- RDB:只包含备份时刻的数据快照,之后的写操作会丢失。
- AOF:通常只丢失1秒的数据。如果差异巨大,请检查备份文件是否正确,或恢复过程中是否有错误。
Q5: 可以同时开启 RDB 和 AOF 持久化吗?
A: 可以,这也是生产环境的常见配置。Redis 启动时会优先使用 AOF 文件来恢复数据,因为它通常更完整。本 SOP 的备份策略也反映了这一点,同时备份两者以提供最大灵活性。
文档维护:本 SOP 由运维团队维护。任何环境变更、经验教训或流程改进都应及时更新到本文档中。
10. Redis Sentinel 生产环境备份与恢复 SOP 脚本
#!/bin/bash
######################################################################
# Redis Sentinel 生产环境备份与恢复 SOP 脚本 (V4.2 - Sentinel 备份增强)
#
# 核心设计理念:
# 1. 数据备份: RDB (从节点) + AOF (主节点/SSH)。
# 2. 配置备份: Redis 配置文件。
# 3. Sentinel 备份 (V4.2 新增):
# - 物理文件: 备份 sentinel.conf (用于重建服务)。
# - 逻辑状态: 备份 Master/Slave/Sentinel 拓扑及 Quorum (用于状态分析)。
# 4. 告警: 模块化支持邮件、飞书、钉钉、企微。
#
# 日期: 2024-06-18
######################################################################
# ==================== 1. 基础配置区域 ====================
# 哨兵连接配置
SENTINEL_HOST="127.0.0.1"
SENTINEL_PORT="26379"
SENTINEL_PASS=""
MASTER_NAME="mymaster"
# Redis 认证信息
REDIS_PASS="Redis@%202601"
# 本地备份服务器配置
BACKUP_ROOT_DIR="/datas/backup/redis"
LOG_FILE="/var/log/redis_sentinel_sop.log"
# Redis 数据节点配置
REDIS_DATA_DIR="/datas/redis/data"
REDIS_SYSTEMD_SERVICE="redis"
REDIS_USER="redis"
# SSH 远程配置
SSH_USER="root"
SSH_KEY_PATH="/root/.ssh/id_rsa"
# 配置文件路径
REDIS_CONFIG_FILE="/datas/redis/conf/redis.conf"
SENTINEL_CONFIG_FILE="/datas/redis/conf/sentinel.conf"
# ==================== 2. 告警模块配置 ====================
ALERT_MAIL_ENABLED=true
ALERT_FEISHU_ENABLED=true
ALERT_DINGTALK_ENABLED=false
ALERT_WECOM_ENABLED=false
MAIL_TO="ops@example.com"
FEISHU_WEBHOOK="https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxxx"
FEISHU_KEYWORD="[Redis告警]"
DINGTALK_WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx"
DINGTALK_KEYWORD="[Redis告警]"
WECOM_WEBHOOK="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxxx"
# ==================== 参数解析 ====================
ACTION="$1"
BACKUP_TYPE="$2"
RESTORE_FILE="$3"
# ==================== 初始化 ====================
mkdir -p "$BACKUP_ROOT_DIR"/{rdb,aof,config,sentinel_files,sentinel_state}
mkdir -p "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"
# 日志函数
log() {
if [ "$1" == "-q" ]; then
shift
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
fi
}
# ==================== 3. 告警模块 ====================
alert_send_mail() {
local subject="$1"
local body="$2"
if [ "$ALERT_MAIL_ENABLED" != true ]; then return 0; fi
if command -v mail &> /dev/null; then
echo "$body" | mail -s "$subject" "$MAIL_TO"
log "[MAIL] 邮件已发送"
fi
}
alert_send_feishu() {
local subject="$1"
local body="$2"
if [ "$ALERT_FEISHU_ENABLED" != true ] || [ -z "$FEISHU_WEBHOOK" ]; then return 0; fi
local msg_content="${FEISHU_KEYWORD}\n\n**${subject}**\n\n${body}"
curl -s -X POST "$FEISHU_WEBHOOK" -H 'Content-Type: application/json' -d "{\"msg_type\":\"text\",\"content\":{\"text\":\"${msg_content}\"}}" > /dev/null
log "[FEISHU] 飞书已发送"
}
alert_send_dingtalk() {
local subject="$1"
local body="$2"
if [ "$ALERT_DINGTALK_ENABLED" != true ] || [ -z "$DINGTALK_WEBHOOK" ]; then return 0; fi
local msg_content="${DINGTALK_KEYWORD}\n\n${subject}\n\n${body}"
curl -s -X POST "$DINGTALK_WEBHOOK" -H 'Content-Type: application/json' -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"${msg_content}\"}}" > /dev/null
log "[DINGTALK] 钉钉已发送"
}
alert_send_wecom() {
local subject="$1"
local body="$2"
if [ "$ALERT_WECOM_ENABLED" != true ] || [ -z "$WECOM_WEBHOOK" ]; then return 0; fi
local msg_content="${subject}\n\n${body}"
curl -s -X POST "$WECOM_WEBHOOK" -H 'Content-Type: application/json' -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"${msg_content}\"}}" > /dev/null
log "[WECOM] 企业微信已发送"
}
send_alert() {
local subject="$1"
local body="$2"
log "[ALERT] $subject"
alert_send_mail "$subject" "$body" &
alert_send_feishu "$subject" "$body" &
alert_send_dingtalk "$subject" "$body" &
alert_send_wecom "$subject" "$body" &
wait
}
# 安全的 Redis CLI 执行
redis_cli_safe() {
local host=$1 port=$2 pass=$3; shift 3
local cmd="redis-cli -h $host -p $port"
[ -n "$pass" ] && cmd="$cmd -a $pass --no-auth-warning"
$cmd "$@"
}
# ==================== 4. 核心业务逻辑 ====================
get_current_node_ip() {
ip addr show | grep -E 'inet\s' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d/ -f1 | head -n 1
}
get_master_node() {
local cmd="redis-cli -h $SENTINEL_HOST -p $SENTINEL_PORT"
[ -n "$SENTINEL_PASS" ] && cmd="$cmd -a $SENTINEL_PASS --no-auth-warning"
$cmd SENTINEL GET-MASTER-ADDR-BY-NAME $MASTER_NAME | tr '\n' ' ' | sed 's/ $//'
}
is_master_node() {
local current_ip=$(get_current_node_ip)
local master_info=$(get_master_node)
local master_ip=$(echo "$master_info" | cut -d' ' -f1)
[ "$current_ip" == "$master_ip" ]
}
get_slaves() {
redis_cli_safe "$SENTINEL_HOST" "$SENTINEL_PORT" "$SENTINEL_PASS" SENTINEL SLAVES $MASTER_NAME 2>/dev/null | \
grep -A 3 "ip" | grep -Ev "ip|port" |grep -v "^--$" | paste - -|awk '{print $1":"$2}'
}
select_best_slave() {
local slaves=$(get_slaves)
local best_slave=""
local max_offset=-1
if [ -z "$slaves" ]; then
log "错误: 未发现任何 Slave 节点。"
send_alert "Redis Backup Failed" "No available slaves."
exit 1
fi
log -q "发现从节点列表: $slaves"
for slave in $slaves; do
local ip=$(echo $slave | cut -d: -f1)
local port=$(echo $slave | cut -d: -f2)
if redis-cli -h $ip -p $port -a "$REDIS_PASS" --no-auth-warning PING &> /dev/null; then
local offset=$(redis-cli -h $ip -p $port -a "$REDIS_PASS" --no-auth-warning INFO replication | grep master_repl_offset | cut -d: -f2 | tr -d '\r')
log -q "检查 Slave [$ip:$port] - Offset: $offset"
if [ -n "$offset" ] && [ $offset -gt $max_offset ]; then
max_offset=$offset
best_slave="$ip:$port"
fi
fi
done
echo "$best_slave"
}
# ==================== 5. 备份逻辑 (核心重构) ====================
backup_rdb() {
log "========== 开始 RDB 备份 =========="
local best_slave=$(select_best_slave)
local slave_ip=$(echo "$best_slave" | cut -d: -f1)
local slave_port=$(echo "$best_slave" | cut -d: -f2)
local timestamp=$(date +%Y%m%d_%H%M%S)
local backup_file="${BACKUP_ROOT_DIR}/rdb/redis_rdb_${slave_ip}_${timestamp}.rdb"
log "正在从 $slave_ip:$slave_port 拉取数据到 $backup_file ..."
if redis-cli -h "$slave_ip" -p "$slave_port" -a "$REDIS_PASS" --rdb "$backup_file" --no-auth-warning; then
log "RDB 拉取完成。校验中..."
if command -v redis-check-rdb &> /dev/null; then
if redis-check-rdb "$backup_file" 2>/dev/null | grep -q "Checksum OK"; then
log "[SUCCESS] RDB 校验通过。"
else
log "[ERROR] RDB 校验失败。"
send_alert "Redis Checksum Failed" "RDB file corrupted."
exit 1
fi
fi
gzip -f "$backup_file"
log "RDB 备份完成: ${backup_file}.gz"
find "$BACKUP_ROOT_DIR/rdb" -name "redis_rdb_*.rdb.gz" -mtime +7 -exec rm -f {} \;
else
log "[ERROR] RDB 拉取失败。"
send_alert "Redis Backup Failed" "Failed to pull RDB."
exit 1
fi
}
backup_aof() {
log "========== 开始 AOF 备份 =========="
local master_info=$(get_master_node)
local master_ip=$(echo "$master_info" | cut -d' ' -f1)
local master_port=$(echo "$master_info" | cut -d' ' -f2)
if [ -z "$master_ip" ]; then
log "错误: 无法从哨兵获取主节点 IP。"
send_alert "Redis AOF Backup Failed" "Cannot get master node."
exit 1
fi
log "目标主节点: $master_ip:$master_port"
log "正在触发主节点 AOF 重写..."
redis-cli -h "$master_ip" -p "$master_port" -a "$REDIS_PASS" --no-auth-warning BGREWRITEAOF &> /dev/null
log "等待 AOF 重写完成..."
local wait_count=0
while [ $wait_count -lt 20 ]; do
local bg_rewrite_status=$(redis-cli -h "$master_ip" -p "$master_port" -a "$REDIS_PASS" --no-auth-warning INFO persistence | grep aof_rewrite_in_progress | cut -d: -f2 | tr -d '\r')
if [ "$bg_rewrite_status" == "0" ]; then break; fi
sleep 1
wait_count=$((wait_count+1))
done
local timestamp=$(date +%Y%m%d_%H%M%S)
local local_backup_file="${BACKUP_ROOT_DIR}/aof/appendonly_${master_ip}_${timestamp}.aof"
local remote_aof_path="${REDIS_DATA_DIR}/appendonly.aof"
if is_master_node; then
log "检测到当前即为主节点,执行本地拷贝..."
cp "$remote_aof_path" "$local_backup_file"
else
log "检测到当前非主节点,尝试通过 SSH 远程拷贝..."
if [ ! -f "$SSH_KEY_PATH" ]; then
log "错误: 未找到 SSH 私钥。"
send_alert "SSH Connection Failed" "SSH Key not found."
exit 1
fi
log "正在从 $master_ip 拉取 AOF 文件..."
scp -i "$SSH_KEY_PATH" -o StrictHostKeyChecking=no ${SSH_USER}@${master_ip}:${remote_aof_path} "$local_backup_file"
if [ $? -ne 0 ]; then
log "错误: SCP 拉取 AOF 文件失败。"
send_alert "SCP Transfer Failed" "Failed to pull AOF from $master_ip"
exit 1
fi
fi
if [ -f "$local_backup_file" ]; then
log "备份文件大小: $(du -h "$local_backup_file" | cut -f1)"
gzip -f "$local_backup_file"
log "AOF 备份完成: ${local_backup_file}.gz"
find "$BACKUP_ROOT_DIR/aof" -name "appendonly_*.aof.gz" -mtime +7 -exec rm -f {} \;
fi
}
backup_redis_config() {
log "========== 开始 Redis 配置备份 =========="
local timestamp=$(date +%Y%m%d_%H%M%S)
local local_backup_file="${BACKUP_ROOT_DIR}/config/redis_local_${timestamp}.conf"
# 1. 本地备份
if [ -f "$REDIS_CONFIG_FILE" ]; then
log "[物理] 备份配置文件: $REDIS_CONFIG_FILE"
cp "$REDIS_CONFIG_FILE" "${BACKUP_ROOT_DIR}/config/redis_local_${timestamp}.conf"
gzip -f "$local_backup_file"
log "Redis 配置备份完成: ${local_backup_file}.gz"
fi
# 2. 远程主节点备份
local master_info=$(get_master_node)
local master_ip=$(echo "$master_info" | cut -d' ' -f1)
local remote_backup_file="${BACKUP_ROOT_DIR}/config/redis_master_${master_ip}_${timestamp}.conf"
if [ -n "$master_ip" ] && [ ! "$master_ip" == "$(get_current_node_ip)" ]; then
scp -i "$SSH_KEY_PATH" -o StrictHostKeyChecking=no ${SSH_USER}@${master_ip}:${REDIS_CONFIG_FILE} "$remote_backup_file" 2>/dev/null
if [ $? -eq 0 ]; then
gzip -f "$remote_backup_file"
log "Redis 配置备份完成: ${remote_backup_file}.gz"
fi
fi
find "$BACKUP_ROOT_DIR/config" -name "redis_*.conf.gz" -mtime +30 -exec rm -f {} \;
}
# ==================== 6. Sentinel 备份 (V4.2 合并版) ====================
backup_sentinel_config() {
log "========== 开始 Sentinel 双重备份 =========="
local timestamp=$(date +%Y%m%d_%H%M%S)
# --- 1. 备份物理配置文件 (用于重建) ---
if [ -f "$SENTINEL_CONFIG_FILE" ]; then
local file_backup="${BACKUP_ROOT_DIR}/sentinel/sentinel_conf_${timestamp}.conf"
log "[物理] 备份配置文件: $SENTINEL_CONFIG_FILE"
cp "$SENTINEL_CONFIG_FILE" "$file_backup"
gzip -f "$file_backup"
log "Sentinel 配置备份完成: $file_backup"
else
log "[物理] 警告: 本地未找到 Sentinel 配置文件 $SENTINEL_CONFIG_FILE"
fi
# --- 2. 备份逻辑状态快照 (用于排障/审计) ---
local state_backup="${BACKUP_ROOT_DIR}/sentinel/sentinel_state_${timestamp}.conf"
log "[逻辑] 生成状态快照..."
{
echo "# Sentinel Logic State Snapshot - $(date)"
echo "# Master Name: $MASTER_NAME"
echo ""
echo "## [MASTER]"
redis_cli_safe "$SENTINEL_HOST" "$SENTINEL_PORT" "$SENTINEL_PASS" SENTINEL MASTER "$MASTER_NAME"
echo ""
echo "## [SLAVES]"
redis_cli_safe "$SENTINEL_HOST" "$SENTINEL_PORT" "$SENTINEL_PASS" SENTINEL SLAVES "$MASTER_NAME"
echo ""
echo "## [SENTINELS]"
redis_cli_safe "$SENTINEL_HOST" "$SENTINEL_PORT" "$SENTINEL_PASS" SENTINEL SENTINELS "$MASTER_NAME"
echo ""
echo "## [CKQUORUM]"
redis_cli_safe "$SENTINEL_HOST" "$SENTINEL_PORT" "$SENTINEL_PASS" SENTINEL CKQUORUM "$MASTER_NAME"
} > "$state_backup"
gzip -f "$state_backup"
log "Sentinel 配置备份状态完成: ${state_backup}.gz"
# 清理旧备份 (配置文件保留30天,状态快照保留7天)
find "$BACKUP_ROOT_DIR/sentinel_files" -name "sentinel_conf_*.conf.gz" -mtime +30 -exec rm -f {} \;
find "$BACKUP_ROOT_DIR/sentinel_state" -name "sentinel_state_*.conf.gz" -mtime +7 -exec rm -f {} \;
log "Sentinel 双重备份完成。"
}
# ==================== 7. 恢复逻辑 ====================
restore_rdb() {
log "========== 开始 RDB 恢复 =========="
[ -z "$RESTORE_FILE" ] && log "错误: 缺少文件路径。" && exit 1
[ ! -f "$RESTORE_FILE" ] && log "错误: 文件不存在。" && exit 1
local temp_file="/tmp/redis_restore_temp.rdb"
if [[ "$RESTORE_FILE" == *.gz ]]; then gunzip -c "$RESTORE_FILE" > "$temp_file" && RESTORE_FILE="$temp_file"; fi
if ! redis-check-rdb "$RESTORE_FILE" 2>/dev/null | grep -q "Checksum OK"; then
log "[ERROR] RDB 校验失败。"; send_alert "Restore Failed" "RDB Checksum error."; exit 1
fi
echo "警告: 将覆盖现有数据,输入 yes 确认:"; read confirm
[ "$confirm" != "yes" ] && exit 0
systemctl stop $REDIS_SYSTEMD_SERVICE
mv "${REDIS_DATA_DIR}/dump.rdb" "${REDIS_DATA_DIR}/dump.rdb.bak_$(date +%s)" 2>/dev/null
cp "$RESTORE_FILE" "${REDIS_DATA_DIR}/dump.rdb"
chown $REDIS_USER:$REDIS_USER "${REDIS_DATA_DIR}/dump.rdb"
systemctl start $REDIS_SYSTEMD_SERVICE
log "RDB 恢复完成。"
}
restore_aof() {
log "========== 开始 AOF 恢复 =========="
[ -z "$RESTORE_FILE" ] && log "错误: 缺少文件路径。" && exit 1
[ ! -f "$RESTORE_FILE" ] && log "错误: 文件不存在。" && exit 1
local temp_file="/tmp/redis_restore_temp.aof"
if [[ "$RESTORE_FILE" == *.gz ]]; then gunzip -c "$RESTORE_FILE" > "$temp_file" && RESTORE_FILE="$temp_file"; fi
if ! redis-check-aof "$RESTORE_FILE" 2>/dev/null | grep -q "OK"; then
log "[ERROR] AOF 校验失败。"; send_alert "Restore Failed" "AOF Checksum error."; exit 1
fi
echo "警告: 将覆盖现有 AOF,输入 yes 确认:"; read confirm
[ "$confirm" != "yes" ] && exit 0
systemctl stop $REDIS_SYSTEMD_SERVICE
mv "${REDIS_DATA_DIR}/appendonly.aof" "${REDIS_DATA_DIR}/appendonly.aof.bak_$(date +%s)" 2>/dev/null
cp "$RESTORE_FILE" "${REDIS_DATA_DIR}/appendonly.aof"
chown $REDIS_USER:$REDIS_USER "${REDIS_DATA_DIR}/appendonly.aof"
systemctl start $REDIS_SYSTEMD_SERVICE
log "AOF 恢复完成。"
}
# ==================== 8. 主流程 ====================
case "$ACTION" in
backup)
case "$BACKUP_TYPE" in
rdb) backup_rdb ;;
aof) backup_aof ;;
redis_config) backup_redis_config ;;
sentinel_config) backup_sentinel_config ;;
config)
backup_redis_config
backup_sentinel_config
;;
all)
backup_rdb
backup_aof
backup_redis_config
backup_sentinel_config
;;
*)
echo "用法: $0 backup [rdb|aof|redis_config|sentinel_config|config|all]"
exit 1
;;
esac
;;
restore)
case "$BACKUP_TYPE" in
rdb) restore_rdb ;;
aof) restore_aof ;;
*) echo "用法: $0 restore [rdb|aof] <file>" ;;
esac
;;
*)
echo "Redis Sentinel SOP Script V4.2 (Enhanced Sentinel Backup)"
echo "================================================================"
echo "备份命令:"
echo " $0 backup rdb # 数据: RDB"
echo " $0 backup aof # 数据: AOF"
echo " $0 backup redis_config # 配置: Redis.conf"
echo " $0 backup sentinel_config # 配置: Sentinel.conf + 状态快照"
echo " $0 backup config # 配置: 所有配置"
echo " $0 backup all # 全量: 数据 + 配置"
echo "恢复命令:"
echo " $0 restore rdb # 数据: RDB"
echo " $0 restore aof # 数据: AOF"
exit 1
;;
esac
更多推荐

所有评论(0)