Redis 运维与监控
从 INFO 开始
INFO 是 Redis 自带的体检报告,所有监控指标最终都来自它。不指定段时输出全部,指定段只输出对应部分:
# 全部
redis-cli -h 127.0.0.1 -p 6379 -a "$REDIS_PASS" --no-auth-warning INFO
# 只看某一段
redis-cli INFO memory
redis-cli INFO stats
redis-cli INFO clients
redis-cli INFO persistence
输出是 key:value 的纯文本,监控系统抓过来解析即可。下面四段是运维最常看的。
memory
| 指标 | 含义 | 怎么看 |
|---|---|---|
used_memory | Redis 分配器分配出去的内存总量 | 和 maxmemory 比,超过 80% 就要警惕 |
used_memory_rss | 操作系统看到的进程实际占用 | 通常略大于 used_memory,差值就是碎片和开销 |
used_memory_peak | 历史峰值 | 判断是否出现过内存尖峰 |
used_memory_dataset | 纯数据占用(扣除开销) | 评估真实数据量 |
mem_fragmentation_ratio | rss / used_memory | 1.0~1.5 正常,大于 1.5 碎片严重 |
maxmemory | 配置的内存上限 | 0 表示不限制,生产必须设 |
maxmemory_policy | 淘汰策略 | 确认是 allkeys-lru 还是 noeviction |
mem_allocator | 内存分配器 | 一般是 jemalloc |
stats
| 指标 | 含义 | 异常信号 |
|---|---|---|
total_connections_received | 累计接受的连接数 | 增长过快说明连接没复用 |
total_commands_processed | 累计处理命令数 | 配合时间算 QPS |
instantaneous_ops_per_sec | 当前每秒命令数 | 突增可能是有大量重试或死循环 |
rejected_connections | 因 maxclients 被拒的连接数 | 大于 0 就是连接打满 |
expired_keys | 累计过期删除的 key 数 | 突增说明有大面积 key 同时到期 |
evicted_keys | 累计因内存淘汰的 key 数 | 持续增长说明内存不够 |
keyspace_hits / keyspace_misses | 命中 / 未命中次数 | 算缓存命中率 |
latest_fork_usec | 最近一次 fork 耗时(微秒) | 上万就要关注持久化卡顿 |
缓存命中率直接算:
hit_rate = keyspace_hits / (keyspace_hits + keyspace_misses)
低于 0.8 就该查了:是缓存时间太短、key 设计有问题、还是有大量不存在的 key 被反复查(缓存穿透)。
clients
| 指标 | 含义 | 说明 |
|---|---|---|
connected_clients | 当前客户端连接数 | 和连接池上限、maxclients 一起看 |
blocked_clients | 阻塞在 BLPOP 等命令上的连接数 | 突然变大可能是队列消费卡住 |
tracking_clients | 客户端缓存(client-side caching)连接数 | 没用到就是 0 |
client_recent_max_input_buffer | 近期最大输入缓冲 | 异常大说明有超大命令 |
client_recent_max_output_buffer | 近期最大输出缓冲 | 异常大说明有慢消费者 |
persistence
| 指标 | 含义 | 正常值 |
|---|---|---|
rdb_last_bgsave_status | 最近一次 RDB 保存结果 | ok |
rdb_last_bgsave_time_sec | 最近一次保存耗时(秒) | 越小越好,突增要查 |
rdb_changes_since_last_save | 距上次保存的写次数 | 持续增大说明快照没成功 |
aof_enabled | 是否开启 AOF | 按规划确认 |
aof_last_write_status | AOF 最近写入状态 | ok |
aof_last_bgrewrite_status | AOF 最近重写状态 | ok |
rdb_last_cow_size | 上次 RDB 期间 COW 复制的字节数 | 越大说明 fork 期间写入越猛 |
rdb_last_bgsave_status:err 和 aof_last_write_status:err 是必须告警的指标——它们意味着持久化已经失败,但 Redis 还在正常接受写请求,看起来一切正常,直到重启才发现数据没了。磁盘满、目录权限错、fork 被 OOM Killer 干掉,都会留下这个状态。
慢查询
Redis 没有慢查询日志文件,而是把超过阈值的命令记在内存里的一个定长队列中,用 SLOWLOG 查看。
配置
# 执行时间超过 10000 微秒(10 毫秒)记为慢查询
redis-cli CONFIG SET slowlog-log-slower-than 10000
# 保留最近 256 条
redis-cli CONFIG SET slowlog-max-len 256
# 时间单位是微秒:0 记录所有命令,负数关闭
两个参数都可以在线改,也会被 CONFIG REWRITE 写回配置文件。阈值怎么定?先按「超过 10ms 就记」跑一周,看慢日志里都是什么命令,再决定是放宽阈值还是优化命令。QPS 很高的实例可以设 5ms,低峰实例设 20ms 也没问题。
分析
redis-cli SLOWLOG GET 10 # 最近 10 条
redis-cli SLOWLOG LEN # 当前条数
redis-cli SLOWLOG RESET # 清空
每条记录长这样:
1) (integer) 14 # 日志 id
2) (integer) 1716883200 # 发生时间戳
3) (integer) 15321 # 执行耗时,微秒
4) 1) "HGETALL" # 命令及参数
2) "user:1001:profile"
5) "10.0.0.21:42318" # 客户端地址
6) "app-server-1" # 客户端名称(设置了才有)
几个判读要点:
- 耗时不包含网络传输和排队时间,只是服务端执行命令的时间。所以「应用侧 RT 高但慢日志没记录」很常见,问题在网络上或者客户端到 Redis 的连接排队。
HGETALL、SMEMBERS、LRANGE、ZRANGE、KEYS、FLUSHALL、SORT是慢日志常客,基本都是大 key 惹的祸。DEL大 key 也慢,它要逐个释放元素。换成UNLINK异步删除。- Lua 脚本整体算一条,脚本里循环几万次也会以一条记录出现,耗时直接是总时间。
按命令聚合一下,能快速定位是哪个业务在捣乱:
redis-cli SLOWLOG GET 100 | grep -A 1 '^4) 1)' | sort | uniq -c | sort -rn | head
内存治理
used_memory 与 rss 的差
used_memory 是分配器视角的「Redis 认为自己用了多少」,used_memory_rss 是操作系统视角的「这个进程实际占了多少物理内存」。两者差值来自三部分:
- 内存碎片:频繁增删不同大小的对象,分配器找不到合适的空闲块,只能向系统要新页。
- 分配器预留:jemalloc 按 size class 分配,释放的内存留在池子里复用,不还给系统。
- 进程自身开销:代码段、栈、连接缓冲区等。
redis-cli INFO memory | grep -E 'used_memory|mem_fragmentation_ratio'
判读规则:
ratio在 1.0~1.5:正常,不用管。ratio大于 1.5:碎片明显。先看是不是有周期性的大 key 增删,再考虑开自动碎片整理:
activedefrag yes
active-defrag-ignore-bytes 100mb
active-defrag-threshold-lower 10
active-defrag-threshold-upper 100
ratio小于 1:危险信号,说明部分内存被换到 swap 了。Redis 对 swap 极其敏感,一旦换出,延迟会从微秒级跳到毫秒级。立刻检查vm.swappiness和机器内存。
大 key 排查
大 key 没有严格定义,通常指 String 超过 10KB、集合类元素超过 5000 个,或者单个 key 占用内存超过 1MB。它的危害是全方位的:读写阻塞、网络拥塞、删除卡顿、持久化变慢、集群数据倾斜。
# 按类型采样,找出每种类型最大的 key
redis-cli --bigkeys
# 按内存占用找(6.0+,需要扫描,比 --bigkeys 慢)
redis-cli --memkeys
# 精确看某个 key 的内存占用,SAMPLES 0 表示全量采样
redis-cli MEMORY USAGE user:1001:profile SAMPLES 0
--bigkeys 用的是 SCAN 采样,不会阻塞,但结果只是「采样到的最大值」,不保证全局最大。它还会顺带输出每种类型的元素数量分布,可以直接看出哪个类型在膨胀。
治理手段:
- 拆分:一个大 Hash 按字段哈希拆成多个小 Hash,一个大 List 按时间或用户拆。
- 异步删除:
UNLINK代替DEL,把释放内存的动作放到后台线程。配合lazyfree-lazy-user-del yes、lazyfree-lazy-expire yes、lazyfree-lazy-server-del yes全局开启。 - 压缩:JSON 换更紧凑的序列化,或者只存必要字段。
- 限长:List 用
LTRIM保留最近 N 条,Set/ZSet 定期清理冷数据。
上线后才发现大 key,迁移和拆分都很痛。code review 时盯住「往集合里无界追加」的写法,比如用户行为流水直接 RPUSH 不设上限、订单商品明细整个塞一个 Hash。加一个「单 key 元素上限」的约定,比事后治理便宜得多。
接入 Prometheus
手工敲 INFO 只能应急,长期监控要把指标采起来。Redis 本身不暴露 Prometheus 格式,中间需要一个 exporter。
redis_exporter 是最常用的一个,单二进制、无状态,直接容器跑:
docker run -d --name redis_exporter \
-p 9121:9121 \
oliver006/redis_exporter:v1.58.0 \
--redis.addr=redis://10.0.0.11:6379 \
--redis.password='Your_Strong_Password_Here'
确认能抓到指标:
curl -s http://127.0.0.1:9121/metrics | grep '^redis_up'
# redis_up 1
然后在 Prometheus 的 scrape_configs 里加一个 job:
scrape_configs:
- job_name: redis
scrape_interval: 15s
static_configs:
- targets:
- 10.0.0.11:9121
labels:
instance: redis-master
env: prod
重启或热加载 Prometheus,在 UI 里查 redis_up 能看到 1 就通了。常用指标:
| 指标 | 对应 INFO | 用途 |
|---|---|---|
redis_up | 进程存活 | 实例是否可达,最基础的告警 |
redis_memory_used_bytes | used_memory | 内存水位 |
redis_memory_max_bytes | maxmemory | 算内存使用率 |
redis_connected_clients | connected_clients | 连接数水位 |
redis_commands_processed_total | total_commands_processed | 算 QPS |
redis_keyspace_hits_total | keyspace_hits | 算命中率 |
redis_keyspace_misses_total | keyspace_misses | 算命中率 |
redis_evicted_keys_total | evicted_keys | 是否有淘汰发生 |
redis_rejected_connections_total | rejected_connections | 连接是否被打满 |
redis_slowlog_length | SLOWLOG LEN | 慢查询堆积情况 |
几条能直接用的告警规则:
groups:
- name: redis
rules:
- alert: RedisDown
expr: redis_up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Redis 实例不可达"
- alert: RedisMemoryHigh
expr: redis_memory_used_bytes / redis_memory_max_bytes > 0.85
for: 5m
labels:
severity: warning
annotations:
summary: "Redis 内存使用率超过 85%"
- alert: RedisEvicting
expr: rate(redis_evicted_keys_total[5m]) > 0
for: 10m
labels:
severity: warning
annotations:
summary: "Redis 正在持续淘汰 key,缓存容量不足"
指标含义、拉模型和 PromQL 的基础,见 Prometheus 核心概念与架构。Grafana 上用现成的 Redis Dashboard(社区有几个常用模板),导入后把数据源指向 Prometheus 即可。
注意 redis_memory_max_bytes 为 0(没设 maxmemory)时,内存水位的除法告警不会触发,这类实例要单独加一条基于 redis_memory_used_bytes 和机器内存的规则。
日常巡检清单
每天扫一眼,比出事再查便宜得多:
# 内存水位、碎片率、淘汰次数
redis-cli INFO memory | grep -E 'used_memory_human|maxmemory_human|mem_fragmentation_ratio'
redis-cli INFO stats | grep -E 'evicted_keys|rejected_connections|expired_keys'
# 连接数与阻塞数
redis-cli INFO clients
# 持久化状态
redis-cli INFO persistence | grep -E 'rdb_last_bgsave_status|aof_last_write_status|rdb_last_bgsave_time_sec'
# 复制状态(有从库时)
redis-cli INFO replication | grep -E 'role|connected_slaves|lag'
# 慢查询 Top
redis-cli SLOWLOG GET 5
小结
- INFO 四段:memory 看水位和碎片,stats 看命中率/淘汰/拒绝连接,clients 看连接数,persistence 看 RDB/AOF 是否成功。
- 慢查询靠
slowlog-log-slower-than记录,SLOWLOG GET分析;10ms 起步,重点盯HGETALL、KEYS、大DEL。 - 内存治理先分清
used_memory和rss,碎片超 1.5 考虑activedefrag,大 key 用--bigkeys/--memkeys找、用UNLINK删、从设计上拆分。 - 监控接入用
redis_exporter暴露指标,Prometheus 抓取后配redis_up、内存水位、淘汰速率三条基础告警。 - 巡检常态化:内存、连接、持久化状态、复制延迟、慢查询,五项每天过一遍。
指标都正常还出问题,那就是故障排查的范畴了,下一篇把 OOM、fork 卡顿、连接打满、主从延迟这些高频故障逐个拆开,见《故障排查与最佳实践》。