欢迎光临
我们一直在努力

Redis 运维实战与排错

一、引言
Redis 基本是缓存、会话、排行榜、轻量队列的「事实标准」。你 AWS 上跑的 ElastiCache for Redis 就是托管形态——但托管不等于「不用懂底层」:当 ElastiCache 出现连接超时、failover 抖动、内存告警时,你得用 Redis 的底层语言跟 AWS 工单对话;而跨账号、特殊模块、合规隔离等场景,又往往要回到自建。
这篇不堆命令,而是把运维最该懂的几条线讲透:单线程模型为什么既快又脆弱、持久化与内存的两难、高可用的三种形态,以及生产里真正高频的排错清单。它和前面 Nginx、防火墙、systemd 几篇是一脉——都是「开源应用落到生产」的实战。
二、原理解析
2.1 单线程事件循环:为什么快,又为什么脆弱
Redis 的命令执行是单线程的。它快,靠三件事叠加:

  • 纯内存操作:绝大部分命令 O(1)/O(log N),没有磁盘寻道。
  • IO 多路复用:用 epoll(Linux)/kqueue 把成千上万连接收敛到一个线程,避免上下文切换。
  • 无锁:单线程天然没有并发竞争,实现简单、不易出错。
    但这里有个关键认知更新:Redis 6.0 引入了 IO 多线程(io-threads),只加速网络读写,命令执行仍然单线程。另外,持久化、主从同步的 RDB 传输由 fork() 出来的子进程或后台线程承担,不阻塞主线程。
    所以结论是:
    • 主线程仍是「一条车道」。任何一个慢命令(大 key、Lua 长脚本、KEYS *)都会阻塞后续所有请求——这是 Redis 延迟毛刺的头号根因。
    • 多核 CPU 对单实例帮不上忙,要靠分片(Cluster)把数据摊到多个实例才能吃满多核。
      2.2 内存与数据结构:编码方式决定 bigkey 危害
      Redis 的 value 不是裸存字符串,而是按数据量和类型选编码(encoding)。比如 Hash/List/Set 在小数据量时用紧凑的 ziplist/listpack 连续存储,一旦超过阈值就转成 hashtable/linkedlist 的指针结构。
      这就引出运维核心概念 bigkey(大 key):
    • 一个 Hash 存了 500 万个 field,或 一个 String 值 200 MB;
    • 单次 HGETALL / DEL 会把单线程卡住几百毫秒甚至秒级;
    • 主从同步、AOF 重写、Cluster 迁移时,bigkey 会放大阻塞和带宽。

    扫描 bigkey(别用 KEYS,用 –bigkeys)

    redis-cli -h 10.0.1.20 -p 6379 –bigkeys

    看某个 key 的编码与元素数

    DEBUG OBJECT myhash
    TYPE myhash ; OBJECT ENCODING myhash
    2.3 持久化:RDB 与 AOF 的两难
    机制
    原理
    优点
    缺点
    运维要点
    RDB
    定时 fork() 子进程做内存快照
    恢复快、文件紧凑、对性能影响小
    可能丢最后一次快照后的数据
    save 900 1 之类阈值;fork 时 COW,内存峰值可能翻倍
    AOF
    追加每一条写命令,可 rewrite 压缩
    数据更安全(everysec 最多丢 1s)
    文件大、恢复慢、fsync 有开销
    appendfsync everysec;定期 BGREWRITEAOF
    混合(推荐)
    RDB 全量 + AOF 增量
    兼顾恢复速度与安全性
    Redis 4.0+
    aof-use-rdb-preamble yes
    fork() 是隐藏雷区:做大内存实例的 RDB/AOF 重写时,fork 本身要复制页表,几十 GB 实例可能卡顿数百毫秒;COW(写时复制)意味着重写期间若大量写入,物理内存可能临时翻倍,触发 OOM。
    2.4 过期与淘汰:别让内存悄悄打满
    过期删除两条腿走路:

    • 惰性删除:访问时才检查过期——不访问就一直占着内存。
    • 定期删除:每秒抽 10 次、每次扫若干 key 主动清。
      所以过期 key 不会立刻消失是正常现象,大量冷 key 过期要靠定期删除慢慢清理。
      内存打满后靠 maxmemory-policy 淘汰(必须设 maxmemory,否则物理内存耗尽被 OOM Kill):
      策略
      含义
      适用
      noeviction
      不淘汰,写报错
      不能丢数据的缓存场景(慎用)
      allkeys-lru
      全库 LRU 淘汰
      通用缓存(最常用)
      volatile-lru
      仅带 TTL 的 LRU
      混用缓存+持久数据
      allkeys-lfu/volatile-lfu
      按访问频率
      有热点倾斜的缓存(4.0+)
      allkeys-random/volatile-random
      随机
      少见
      volatile-ttl
      先淘汰快过期的
      少见
      经验:缓存场景一律 allkeys-lru 或 allkeys-lfu,且务必设 maxmemory。用 noeviction 等于把炸弹埋给自己。
      2.5 高可用三形态:主从 → 哨兵 → Cluster
      主从复制(异步)
      master ──异步传播──> replica1
      └─> replica2
      全量同步(RDB 传输)+ 部分重同步(backlog 偏移量)
      注意:复制是异步的,master 宕机可能丢最后一点写

    哨兵 Sentinel(自动 failover)
    3 个 sentinel 监控 master,quorum 判定客观下线 → 选新 master → 通知客户端
    解决「主从要人工切」的问题

    Redis Cluster(数据分片 + 高可用)
    16384 个 slot,按 CRC16(key)%16384 映射到节点
    每个 slot 一主多从,节点间 gossip 互通
    客户端请求错节点返回 MOVED(槽真搬家)/ ASK(迁移中临时重定向)
    三、实战演示
    3.1 生产级单机配置(docker-compose)

    docker-compose.redis.yml

    services:
    redis:
    image: redis:7.2-alpine
    command:
    – redis-server
    – –requirepass ${REDIS_PWD}
    – –maxmemory 4gb
    – –maxmemory-policy allkeys-lru
    – –appendonly yes
    – –aof-use-rdb-preamble yes
    – –save 900 1
    – –save 300 100
    – –slowlog-log-slower-than 10000 # 慢于 10ms 记慢日志
    – –slowlog-max-len 128
    – –tcp-keepalive 60
    ports:
    – “6379:6379”
    volumes:
    – redis-data:/data
    deploy:
    resources:
    limits:
    memory: 5g # 给 AOF/RDB 重写留 headroom,别贴满 maxmemory
    volumes:
    redis-data:
    关键:容器内存 limit 必须 > maxmemory(建议 1.2~1.5 倍),否则重写/COW 时物理内存超 limit 被 OOM Kill。
    3.2 主从复制

    replica 的配置里加(或命令行 REPLICAOF)

    replicaof 10.0.1.20 6379
    masterauth
    replica-read-only yes # 从库只读(默认)

    查看复制状态

    redis-cli INFO replication

    role:slave / master_link_status:up / master_sync_in_progress:0 都是关键指标

    3.3 哨兵(三节点,防脑裂)

    sentinel.conf(每个哨兵一份,monitor 指向 master)

    sentinel monitor mymaster 10.0.1.20 6379 2
    sentinel auth-pass mymaster
    sentinel down-after-milliseconds mymaster 5000
    sentinel failover-timeout mymaster 10000

    防脑裂:master 至少要有 1 个 replica 且延迟 < 10s 才接受写

    min-replicas-to-write 1
    min-replicas-max-lag 10
    3.4 Redis Cluster(三主三从)

    启动 6 个节点(–cluster-enabled yes),然后建簇

    redis-cli –cluster create
    10.0.1.11:6379 10.0.1.12:6379 10.0.1.13:6379
    10.0.1.14:6379 10.0.1.15:6379 10.0.1.16:6379
    –cluster-replicas 1

    查看槽位分配

    redis-cli -h 10.0.1.11 -p 6379 CLUSTER SLOTS

    扩容后重新分片(把 1000 个 slot 从 11 迁到 新节点 17)

    redis-cli –cluster reshard 10.0.1.11:6379
    –cluster-from –cluster-to
    –cluster-slots 1000 –cluster-yes
    3.5 连接池(客户端侧,常被忽略)
    Redis 单线程,连接数 ≠ 并发能力,但连接本身吃内存(每连接约数 KB~几十 KB)。常见坑是应用端没用连接池,每次请求新建连接 → maxclients 打满(ERROR max number of clients reached)。

    • Jedis:用 JedisPool,maxTotal 控制在合理范围(如 20~50/实例),testOnBorrow 按需开。
    • Lettuce:默认基于 Netty 的共享连接 + 异步,天然高并发;但事务/阻塞命令需注意连接隔离。
    • 务必设置 timeout(服务端 timeout 300 回收空闲连接)+ 客户端 tcp-keepalive。
      3.6 与 AWS ElastiCache 对照
      自建 Redis
      ElastiCache for Redis
      备注
      一台 EC2 跑 redis-server
      托管节点,自动补丁/重启
      省运维,但黑盒
      自己配哨兵/Cluster
      Multi-AZ + 自动 failover
      托管 failover 更快更稳
      redis-cli 直连
      用 VPC 端点 + 安全组
      同样要管网络/安全组
      自定义参数
      Parameter Group
      调 maxmemory-policy 等要靠参数组
      什么场景仍要自建:跨账号/跨 VPC 的合规隔离、要用 Redis 社区版不支持的模块、或要做 ElastiCache 不做的事(如特定版本、定制编译)。其余一律上 ElastiCache,把 failover、备份交给 AWS。
      四、排错实战(重点)
      4.1 内存打满 / OOM Kill
      现象:redis-cli INFO memory 的 used_memory 逼近 maxmemory,随后写报错或进程被 OOM。
      排查:
      redis-cli INFO memory | grep -E “used_memory_human|maxmemory_human|mem_fragmentation_ratio”
      redis-cli –bigkeys # 找大 key
      redis-cli INFO keyspace # 各库 key 数
      根因与对策:
    • 没设 maxmemory → 物理内存耗尽被系统 OOM Kill。立刻设 maxmemory + LRU 策略。
    • mem_fragmentation_ratio 远大于 1.5 → 内存碎片,可执行 MEMORY PURGE(jemalloc)或重启实例整理。
    • bigkey 占大头 → 拆分(Hash 按 field 二次分片、大 List 改用多个 List)。
      4.2 bigkey / hotkey
    • bigkey:用 –bigkeys 或 DEBUG OBJECT 定位;危害是阻塞单线程 + 放大同步/迁移。
    • hotkey:某个 key 被海量请求打(如爆款商品库存)。redis-cli –hotkeys(需 maxmemory-policy 为 LFU 类才能统计)或看 INFO commandstats + 业务埋点。
      对策:bigkey 拆分;hotkey 本地缓存 + 多副本(key 加随机后缀打散到多个副本)+ 限流。
      4.3 延迟毛刺(slowlog + latency monitor)
      现象:偶发 P99 延迟从 1ms 跳到几百 ms。
      redis-cli SLOWLOG GET 20 # 看慢命令(含执行时长、时间戳)
      redis-cli CONFIG SET latency-monitor-threshold 100 # 开启延迟监控(100ms)
      redis-cli LATENCY LATEST
      redis-cli LATENCY GRAPH command
      常见根因:bigkey 操作、KEYS */FLUSHALL 误操作、fork 阻塞(持久化)、操作系统 swap(必查)、跨可用区网络抖动、CPU 竞争。
      swap 是隐形杀手:Redis 一旦被换页到磁盘,单线程会因缺页暂停几百 ms。务必关 swap(swapoff -a)或设 vm.swappiness=1,并保证内存充足。
      4.4 连接数暴涨 / maxclients 报错
      redis-cli INFO clients | grep -E “connected_clients|blocked_clients”
      redis-cli CLIENT LIST # 看每连接来源 IP、空闲时间、命令
      根因:应用端没用连接池(每次新建连接)、连接泄漏(没关)、timeout 设 0(永远不回收空闲连接)。
      对策:上连接池、timeout 300、安全组限制来源 IP、maxclients 适当调大(注意 ulimit)。
      4.5 AOF/RDB 加载慢、fork 阻塞
    • 重启恢复时 AOF 比 RDB 慢(要重放命令)。大实例建议开混合持久化。
    • fork 慢:INFO persistence 看 latest_fork_usec,几十 GB 实例可达数百 ms。大内存实例考虑分片降低单实例体量。
    • COW 期间写入放大导致内存翻倍 → OOM:给宿主机/容器留足 headroom,避开业务高峰做 BGREWRITEAOF。
      4.6 Cluster 槽位 / 重定向问题
    • CLUSTERDOWN:有 slot 未分配(reshard 中断、节点失联)。CLUSTER SLOTS + CLUSTER NODES 定位缺失 slot,补 CLUSTER ASSIGNSLOT / CLUSTER SETSLOT。
    • MOVED vs ASK:MOVED = 槽已永久迁移,客户端应更新路由;ASK = 迁移进行中临时重定向,客户端本次请求跟过去、但路由表不变。
    • 客户端没开 Cluster 模式:普通客户端连 Cluster 会被 MOVED 拒绝,必须用支持 Cluster 的客户端(Jedis Cluster / Lettuce Cluster)。
      4.7 脑裂(split-brain)
      网络分区导致出现双 master 同时接受写,分区恢复后数据冲突/丢失。
      对策:min-replicas-to-write 1 + min-replicas-max-lag 10,让 master 在失去副本时拒绝写,以可用性换一致性。ElastiCache 的 Multi-AZ 也用类似机制。
      4.8 KEYS 卡死 → 用 SCAN
      KEYS * 在百万 key 上会阻塞单线程几十秒,线上严禁。改用游标迭代:
      redis-cli –scan –count 1000 | grep “session:*”

    或代码里 SCAN 游标

    同理 FLUSHALL/DEL 大 key 用 UNLINK(异步惰性删除,不阻塞)。
    4.9 与 ElastiCache 排错衔接
    ElastiCache 出问题时,底层还是这套语言:

    • 连接超时/failover 抖动:查 Event(控制台)+ 节点重启,多半是内存压力触发托管重启或网络分区 → 回到 4.1/4.7。
    • CPU 100%:单分片单线程瓶颈 → 看 EngineCPUUtilization,需增加分片摊负载(ElastiCache 在线 reshard)。
    • 内存逐出/写失败:Evictions 指标上升、CurrItems 陡降 → 回到 4.1 的淘汰策略与 bigkey。
      五、总结
      Redis 运维的核心就三条线,记住就不会慌:
  • 单线程 = 一条车道:任何慢命令、bigkey、KEYS 都会堵全车。监控靠 slowlog + latency-monitor,预防靠拆 key、用 SCAN/UNLINK、避免跨可用区。
  • 内存是生命线:必须设 maxmemory + LRU/LFU 策略;给持久化/COW 留 headroom;盯 mem_fragmentation_ratio 和 swap。
  • 高可用按需选形态:主从(只读扩展)→ 哨兵(自动 failover)→ Cluster(分片扛多核)。托管场景上 ElastiCache,自建只留给合规/特殊模块。
    速查表
    问题
    第一手命令
    根因方向
    内存打满
    INFO memory / –bigkeys
    没设 maxmemory、bigkey、碎片
    延迟毛刺
    SLOWLOG GET / LATENCY GRAPH
    bigkey、swap、fork、网络
    连接爆
    INFO clients / CLIENT LIST
    无连接池、泄漏、timeout=0
    集群不可用
    CLUSTER SLOTS / CLUSTER NODES
    slot 缺失、MOVED/ASK、客户端模式
    启动慢
    看 AOF 大小 / latest_fork_usec
    AOF 重放、fork 阻塞
  • 赞(0)
    未经允许不得转载:171主机测评 » Redis 运维实战与排错
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址