Redis 内存防崩溃优化方案
Redis 内存防崩溃优化方案
2026-08-21 0 评论 0 阅读 0 点赞

Redis 内存防崩溃优化方案

daimafengzi
2026-08-21 / 0 评论 / 0 阅读 / 正在检测是否收录...

Redis 内存防崩溃优化方案

目标:让 Redis 永远有硬上限,系统永远不进 Swap,事故提前告警而非事后重启。
⚠️ 全文命令中的密码已替换为占位符 你的密码,执行时请换成真实密码;redis-cli 路径每台服务器不同,请先按【附一】查询替换。

附一、先查询 redis-cli 路径(每台服务器不同,纯查询、不影响系统)

下面都是只读查询命令,不会改动任何东西,放心执行:

# 1. 看系统 PATH 里是否已能直接调用(最常用)
which redis-cli

# 2. 看正在运行的 redis-server 二进制在哪,redis-cli 通常在同目录的 bin 下
ps -ef | grep redis-server

# 3. 常见安装目录直查(哪个有输出就用哪个)
ls -l /usr/local/redis/bin/redis-cli 2>/dev/null
ls -l /usr/bin/redis-cli 2>/dev/null
ls -l /opt/redis/bin/redis-cli 2>/dev/null

# 4. 全盘找(只读扫描,不影响系统;结果里取第一个存在的路径)
find / -name redis-cli -type f 2>/dev/null | head

查到后,把下文命令里的 /usr/local/redis/bin/redis-cli 整体换成你查到的实际路径即可。


一、根因结论(一句话)

Redis 没有有效的内存上限(或上限过大)→ 内存无限增长 → 物理内存耗尽 → Swap 打满(100%) → 页面置换风暴 → CPU 99% → 系统假死。

典型崩溃时间线:
Redis 吃满内存Swap 96%Swap 100%触发事件,CPU 2%→42%CPU 99% / 高负载 / 假死人工重启

核心矛盾不是"Redis 用了多少",而是"它能不能无限用"。 只要给它一个硬上限并留足系统余量,同样的负载绝不会再把机器拖死。


二、设计三原则

#原则作用
1给 Redis 设硬上限 maxmemory满则"驱逐旧数据",而非"撑爆系统内存"
2系统永远留余量,绝不进 SwapSwap 一旦打满,Redis 延迟暴涨、CPU 被置换吞掉
3合理监控水位,提前感知风险把事故消灭在苗头,而不是 100% 假死
说明:原则 2 的"绝不进 Swap"是靠原则 1 把 Redis 框在合理上限内实现的(详见第三节),不需要去动系统内核参数。

三、maxmemory 设置(最关键的一步)

3.1 计算公式(务必满足)

maxmemory ≤ (物理内存 × 0.85) − 其他进程常驻内存
  • 例:物理 31G,其他常驻进程(应用、数据库、Agent 等)合计约 6G
  • 安全上限:31 × 0.85 − 6 ≈ 20G。即 Redis 最多给到 20G 也不会逼系统用 Swap。

3.2 推荐值

  • 按"当前工作集 × 1.5"留余量防突增。若当前稳定 8G,则 maxmemory = 12G(12884901888 字节)
  • 12G + 6G 其他 ≈ 18G,远小于 31G,系统永远有空闲 → 永不 Swap

3.3 设置命令(路径请先按【附一】查询替换)

# 临时生效(立即止血)
/usr/local/redis/bin/redis-cli -a '你的密码' CONFIG SET maxmemory 12884901888

# 验证
/usr/local/redis/bin/redis-cli -a '你的密码' CONFIG GET maxmemory

3.4 ⚠️ 持久化(极易踩坑)

若 redis 启动时没有带配置文件参数(如 redis-server 0.0.0.0:6379),它是用默认配置启动的,CONFIG REWRITE 可能新建文件但重启仍会丢配置

必须找到真正的启动入口并写进去(以下为只读查询,定位用):

# 看是不是 systemd 托管
systemctl cat redis 2>/dev/null
systemctl cat redis-server 2>/dev/null

# 或看 init 脚本 / 启动脚本里有没有 redis-server
ls -l /etc/init.d/ | grep -i redis
grep -r "redis-server" /etc/rc.local /root/*.sh /home/*/*.sh 2>/dev/null

找到后,在启动配置里显式加一行 maxmemory 12884901888(或指向一个含该项的 redis.conf),再重启验证 CONFIG GET maxmemory 是否持久生效。不要只依赖 CONFIG REWRITE


四、淘汰策略(maxmemory-policy)选择

4.1 策略对比

策略淘汰范围适用场景风险
noeviction不淘汰,写报错不允许丢数据写阻塞,不可用
allkeys-lru全部 key,按最近最少用纯缓存永久 key 也会被删
allkeys-lfu全部 key,按最不常用纯缓存(更优)永久 key 也会被删
volatile-lru/lfu有 TTL 的 key缓存+持久混放无 TTL 的 key 永不淘汰,可能涨爆
volatile-ttl仅即将过期的 key同 volatile同上
选型建议:纯缓存实例用 allkeys-lfu(比 LRU 更懂"常用",缓存命中更好);若缓存与不可重建的持久数据混放,应改用 volatile-lfu 并给缓存 key 补 TTL,或干脆把持久数据拆到独立实例。

五、OOM 防护(可选兜底,保护关键进程)

本节为"最后兜底",非必须;主防线仍是 maxmemory + 不进 Swap。以下只是调整 OOM 优先级(不改内核行为),风险低。

万一再次发生内存争抢,确保关键业务进程(数据库 / 应用)不被 OOM killer 误杀,而让 Redis 优先被回收:

# 降低关键进程的 OOM 被杀概率(数值越小越安全,-1000 几乎永不被杀)
echo -500 > /proc/<关键进程_pid>/oom_score_adj
# 提高 redis 被杀优先级(让它先被回收,而不是拖死整机)
echo 500 > /proc/<redis_pid>/oom_score_adj   # 换成实际 redis PID

六、架构层面(长期根治,均为建议、非强制命令)

  1. 缓存 / 持久数据分离:Redis 只放可重建的缓存;Session、锁、业务状态搬到其他存储或独立实例。
  2. 服务器图形界面:生产服务器通常不需要图形界面,如确认无用,可禁用其自启动以省内存(不要盲目卸载,避免牵连依赖)。
  3. 多实例隔离:若多种业务共用一台 Redis,按业务拆实例,单实例故障域更小、上限更好控。

七、应急响应 Runbook(若再次假死)

  1. 不要反复重启:重启会重新加载/重建数据,可能二次冲击。先确认 Swap 状态。
  2. 快速查看(只读):

    free -h; swapon --show        # 看 Swap 是否打满
  3. 若 Redis 已无响应且内存失控:可 redis-cli SHUTDOWN NOSAVE(避免写坏 RDB),再按本方案设好 maxmemory 后拉起。
  4. 拉起后第一时间 CONFIG GET maxmemory + INFO memory 验证上限已生效。

八、落地检查清单

  • [ ] 先按【附一】查到本机 redis-cli 真实路径,替换全文命令
  • [ ] 确认缓存实例是否纯缓存(决定 allkeys-lfu 还是 volatile-lfu)
  • [ ] CONFIG SET maxmemory 12884901888(12G,立即生效)
  • [ ] 找到 redis 真实启动入口,把 maxmemory 写进配置/启动参数(持久化)
  • [ ] 切换/确认 maxmemory-policy(纯缓存→allkeys-lfu)
  • [ ] 配置 OOM 优先级,保护关键进程(可选)
  • [ ] 验证:重启 redis 后 CONFIG GET maxmemory 仍为 12G

附:命令安全提示

命令行用 -a 明文密码会触发告警且密码进 history。建议改用环境变量(把 你的密码 换成真实密码):

export REDISCLI_AUTH='你的密码'
/usr/local/redis/bin/redis-cli CONFIG GET maxmemory

附:检查脚本

#!/bin/bash
# redis_health_check.sh
# 用途:巡检 Redis 内存水位 + 系统 Swap,超标即打印告警(可接邮件/钉钉)
# 用法:crontab 每 5 分钟执行一次,输出追加入日志
#   */5 * * * * /usr/local/redis/redis_health_check.sh >> /var/log/redis_health.log 2>&1

# ===== 配置区(按需修改)=====
# 密码:执行前请换成真实密码(或改用 export REDISCLI_AUTH 方式,避免明文)
PASS='你的密码'
HOST=127.0.0.1
PORT=6379

# redis-cli 路径:每台服务器不一样,优先用 which 自动查找;找不到再手动改成实际路径
# 也可先手动查询: which redis-cli
#                 ls -l /usr/local/redis/bin/redis-cli 2>/dev/null
#                 ps -ef | grep redis-server
#                 find / -name redis-cli -type f 2>/dev/null | head
REDIS_CLI=$(command -v redis-cli 2>/dev/null)
if [ -z "$REDIS_CLI" ]; then
  REDIS_CLI="/usr/local/redis/bin/redis-cli"   # 自动找不到时,改成你查到的真实路径
fi

TS=$(date '+%Y-%m-%d %H:%M:%S')
echo "===== [$TS] Redis 健康巡检 ====="

# ---- 1. Redis 内存使用率 ----
USED=$($REDIS_CLI -h $HOST -p $PORT -a "$PASS" INFO memory 2>/dev/null | awk -F: '/^used_memory:/{print $2}' | tr -d '\r')
MAX=$($REDIS_CLI -h $HOST -p $PORT -a "$PASS" INFO memory 2>/dev/null | awk -F: '/^maxmemory:/{print $2}' | tr -d '\r')
EVI=$($REDIS_CLI -h $HOST -p $PORT -a "$PASS" INFO stats 2>/dev/null | awk -F: '/^evicted_keys:/{print $2}' | tr -d '\r')

if [ -n "$MAX" ] && [ "$MAX" -gt 0 ] 2>/dev/null; then
  RATIO=$(awk "BEGIN{printf \"%.2f\", $USED/$MAX*100}")
  echo "Redis 内存使用率: ${RATIO}%   (used=$USED  max=$MAX)"
  # 超过 85% 逼近 maxmemory,即将触发驱逐
  if awk "BEGIN{exit !($RATIO>85)}"; then
    echo "  [告警] 内存使用率大于85%,逼近 maxmemory,即将触发 key 驱逐!"
  fi
else
  echo "  [错误] maxmemory 未设置或为 0 —— Redis 可无限吃内存,存在崩溃风险!"
fi

echo "累计驱逐 key 数 (evicted_keys): ${EVI:-未知}"

# ---- 2. 系统 Swap 使用率 ----
SWAP_TOTAL=$(free | awk '/Swap:/{print $2}')
SWAP_USED=$(free | awk '/Swap:/{print $3}')
if [ -n "$SWAP_TOTAL" ] && [ "$SWAP_TOTAL" -gt 0 ] 2>/dev/null; then
  SWAP_RATIO=$(awk "BEGIN{printf \"%.2f\", $SWAP_USED/$SWAP_TOTAL*100}")
  echo "系统 Swap 使用率: ${SWAP_RATIO}%"
  if awk "BEGIN{exit !($SWAP_RATIO>50)}"; then
    echo "  [严重] Swap 使用率大于50%,系统已进入危险区,立即排查内存占用!"
  fi
else
  echo "系统未启用 Swap(或未检测到)—— 无需关注 Swap 水位"
fi

echo ""
0

评论 (0)

取消