Redis 异常重启故障复盘
故障时间:2026-04-22 06:09
影响范围:游戏测试服 Redis 连接报错
复盘时间:2026-04-22
一、问题现象
早上上班发现监控告警,查看 LogViewer 发现凌晨 06:09 集中爆发一批 Redis 连接错误:
Redis connection refused / Connection reset by peer错误日志显示大量服务实例同时失去 Redis 连接,且持续时间很短(几十秒内恢复),这不符合正常的网络抖动或 Redis 自身崩溃的特征。
二、初步排查
2.1 查看 Redis 服务状态
journalctl-uredis-server--since"2026-04-22 06:00:00"--until"2026-04-22 06:15:00"输出关键日志:
Apr 22 06:09:24 game systemd[1]: Stopping redis-server.service - Advanced key-value store... Apr 22 06:09:25 game systemd[1]: redis-server.service: Deactivated successfully. Apr 22 06:09:25 game systemd[1]: Stopped redis-server.service - Advanced key-value store. Apr 22 06:09:25 game systemd[1]: Starting redis-server.service - Advanced key-value store... Apr 22 06:09:36 game systemd[1]: Started redis-server.service - Advanced key-value store.分析:
- Redis 经历了stop → start的完整生命周期
- 停止和启动间隔仅11 秒,说明是被外部信号触发的正常关闭
- 如果是 Redis 自身崩溃,日志会显示
Segmentation fault或Aborted;如果是 OOM,日志会显示OOM killed
结论:Redis 不是「崩溃」,而是「被关闭后重启」。需要找出是谁发的停止信号。
三、定位根因
3.1 第一步:Redis 自己想停吗?
确认了 Redis 是被关闭而非崩溃后,第一反应是:Redis 自己想停的吗?
检查 Redis 服务自身状态:
systemctl status redis-server预期结果:
- 如果是 Redis 自身崩溃,
Active:会显示failed或最近一次启动时间异常 - 如果是 OOM 被 kill,
dmesg | grep -i redis或journalctl -b | grep -i oom应该能看到Out of memory相关日志
实际情况:
Redis 服务状态正常,最近一次启动时间在凌晨 06:09:36,说明Redis 自己没有「想」停,是被叫停的。
3.2 第二步:是不是系统层面的操作?
既然不是 Redis 自身问题,接下来怀疑几个方向:
- 定时任务(cron)有人写了凌晨重启 Redis 的脚本?
- 系统服务(systemd)其他 service 的依赖导致的?
- 系统更新(apt)自动更新动了 Redis 的包?
先检查cron 定时任务:
# 查看所有用户的 crontabcrontab-lsudocat/etc/crontabsudols-la/etc/cron.d/结果:没有发现任何重启 Redis 的 cron 任务。
3.3 第三步:检查 systemd 定时器
继续排查systemd timer——这是最容易被忽略的「定时搞事情」选手:
# 列出所有活跃的 timersystemctl list-timers--all输出内容:
发现可疑点:apt-daily-upgrade.timer上次触发时间是06:09:10,而 Redis 停止时间是06:09:24——只差了 14 秒!
这时间吻合度太巧了,强烈怀疑是它干的。
3.4 第四步:直接验证 apt-daily-upgrade.timer
为了不靠「感觉」下结论,直接用数据说话:
systemctl show apt-daily-upgrade.timer-pLastTriggerUSec-pNextElapseUSecRealtime-pTriggers输出:
NextElapseUSecRealtime=Thu 2026-04-23 06:16:35 CST LastTriggerUSec=Wed 2026-04-22 06:09:10 CST Triggers=apt-daily-upgrade.service理论依据:
systemctl show <unit>是查看 systemd 单元属性的一手工具,比systemctl status更底层,返回的是运行时状态而非解析后的格式。
关键字段含义:
| 字段 | 含义 |
|---|---|
LastTriggerUSec | 上一次触发时间(UTC 时间戳,可转换为本地时间) |
NextElapseUSecRealtime | 下一次触发时间(日历时间) |
Triggers | 该 timer 触发后会启动哪个 service |
时间吻合度分析:
| 事件 | 时间 |
|---|---|
| apt-daily-upgrade.timer 触发 | 06:09:10 |
| Redis 收到停止信号 | 06:09:24 |
| 时间差 | 14 秒 |
14 秒的间隔非常合理——apt-daily-upgrade.timer 触发后,systemd 启动 apt-daily-upgrade.service,该服务执行apt-get upgrade更新系统包。更新涉及 Redis 依赖的动态库或 Redis 本身时,systemd 会根据依赖关系自动重启 Redis。
3.5 第五步:理论依据——apt-daily-upgrade.timer 是什么?
理论依据:
apt-daily.timer和apt-daily-upgrade.timer是 Ubuntu/Debian 系统内置的自动更新机制:
| Timer | 作用 | 默认触发时间 |
|---|---|---|
apt-daily.timer | 每日执行apt update刷新软件包索引 | 每天 06:00 左右 |
apt-daily-upgrade.timer | 每日执行apt upgrade安装安全更新 | 每天 06:00 左右(比 daily 晚几分钟) |
这些 timer 的触发时间由/lib/systemd/system/apt-daily.timer和/etc/crontab定义。系统会根据上一次触发时间 + 24小时计算下一次触发时间,但由于 jitter(随机偏移)和系统启动延迟,实际触发时间会有几分钟波动。
引用:Debian/Ubuntu 官方文档《AutomaticUpdates》明确指出这两个 timer 是系统级别的自动更新机制,会在触发时执行 apt 操作,可能导致依赖包更新和服务重启。
3.6 第六步:日志交叉验证(需要 sudo 权限)
由于初始排查没有 root 权限,让运维同学执行以下命令 100% 确认根因:
# 查看 apt-daily-upgrade 和 redis-server 在该时间段的日志sudojournalctl-uapt-daily-upgrade-uredis-server\--since"2026-04-22 06:00:00"\--until"2026-04-22 06:20:00"\-oshort-iso# 查看 dpkg 包管理日志中的软件包变更sudogrep-E"2026-04-22 06:0[0-9]:|redis|redis-server|upgrade|install|configure"/var/log/dpkg.log理论依据:
journalctl -u -u:
-u可以叠加,用于关联多个服务的日志。--since/--until指定时间窗口,-o short-iso输出 ISO 格式时间戳便于对比。/var/log/dpkg.log:这是 Debian 包管理器的操作日志,记录每次
apt/dpkg安装、配置、升级的具体软件包名称和时间。格式为:2026-04-22 06:09:15 status installed redis-server:amd64 6.0.16-1 2026-04-22 06:09:16 configure redis-server 6.0.16-1
通过关联dpkg.log的时间和apt-daily-upgrade.service的日志,可以精确定位是哪个软件包触发了 Redis 重启。
四、根因确认
4.1 事件链路
apt-daily-upgrade.timer (06:09:10 触发) ↓ apt-daily-upgrade.service 启动 ↓ 执行 apt-get upgrade 安装待更新包 ↓ 更新 Redis 相关包或依赖库(如 libsystemd、glibc 等) ↓ dpkg 触发 postinst 脚本 → systemctl daemon-reload ↓ systemd 重新加载 redis-server.service ↓ redis-server 被停止 → 立即启动 ↓ 业务侧 Redis 连接报错(06:09:24 - 06:09:36)五、解决方案
5.1 当前处理:关闭 apt-daily-upgrade.timer
目前项目处于测试阶段,为了避免自动更新对测试的影响,直接关闭该 timer:
# 停止并禁用 apt-daily-upgrade.timersudosystemctl stop apt-daily-upgrade.timersudosystemctl disable apt-daily-upgrade.timer# 确认状态systemctl status apt-daily-upgrade.timer验证:
# 确认所有 timer 状态systemctl list-timers--all|grepapt输出应为空,说明 apt-daily-upgrade 定时器已停止。
5.2 后续建议(上线前)
正式上线前建议评估以下方案:
| 方案 | 做法 | 适用场景 |
|---|---|---|
| 方案 A:调整触发时间 | 将触发时间调整到业务低峰期(如凌晨 04:00),并设置维护窗口 | 允许短暂服务中断 |
| 方案 B:分步升级 | 使用Unattended-Upgrade的MinimalSteps选项,减少服务中断 | 需要精细控制 |
| 方案 C:服务保护 | 在redis-server.service中设置Restart=on-failure,减少中断影响 | 无法控制更新时机 |
六、经验总结
6.1 排查思路
| 阶段 | 操作 | 目的 |
|---|---|---|
| 现象观察 | 查看 Redis 日志,确认是 stop→start 而非 crash | 缩小排查范围:不是 Redis 自身问题 |
| 排除 cron | 检查 crontab 和 cron.d | 排除定时任务误操作 |
| 扫描 timer | systemctl list-timers 快速定位可疑定时器 | 建立时间关联 |
| 数据验证 | systemctl show 确认 timer 触发时间 | 量化吻合度 |
| 日志交叉验证 | journalctl + dpkg.log 确认根因 | 100% 闭环 |
6.2 关键命令速查
# 1. 查看服务状态systemctl status<service># 2. 检查 cron 定时任务crontab-lsudocat/etc/crontabsudols-la/etc/cron.d/# 3. 列出所有活跃的 timersystemctl list-timers--all# 4. 查看 timer 的触发时间systemctl show<timer>-pLastTriggerUSec-pNextElapseUSecRealtime-pTriggers# 5. 关联查看多服务日志journalctl-u<service1>-u<service2>--since"HH:MM:SS"# 6. 查看包管理变更历史grep"<date>"/var/log/dpkg.log6.3 教训
- 测试环境也要关注系统级定时任务:apt-daily-upgrade 是 Ubuntu 默认开启的,即使在测试机上也可能触发
- 排查要「先猜后证」:根据时间吻合度大胆假设,再用日志小心求证
- 没有 root 权限也能排查:systemctl show 和 systemctl list-timers 不需要 root,只需读权限
七、参考文档
- Ubuntu AutomaticUpdates 官方文档
- systemd.timer 中文手册
- Debian apt/dpkg 日志说明