做后端开发这些年,Redis 基本是我每套系统里最先引入的组件之一。缓存层、分布式锁、排行榜、计数器、热点数据、会话保持,几乎都绕不开它。很多同学对 Redis 的印象停留在“看文档能跑通”,但一旦进入生产环境,内存突增、主从切换失败、频繁持久化阻塞、缓存穿透打垮数据库,每一个坑都让人头大。这篇内容不打算给你铺一张庞大而空洞的知识网,而是把我实际用 Redis 时反复验证过的东西摊开讲:底层到底怎么存数据、RDB/AOF 怎么选、生产部署怎么做、主从哨兵怎么搭、线上问题怎么排,以及那些文档里不会写但很值得留意的细节。无论你是刚开始接触 Redis,还是已经在项目里踩过坑,照着这份思路走,能省下不少摸索时间。
1. 先把 Redis 的定位说清楚:一个内存里的数据结构服务器
1.1 为什么大多数团队第一反应就是 Redis
我们先回到最根本的问题:Redis 到底是干什么的?官方称呼是 in-memory data structure store,意思是“基于内存的数据结构存储”,但它同时也能做消息队列、分布式锁、位图统计,甚至当搜索引擎的辅助索引。核心点就两个:一是数据放在内存里,读写速度可以到十万乃至十万百万级 QPS;二是它操作的不是普通 KV,而是字符串、哈希、列表、集合、有序集合这些有结构的数据。
也正因为内存是真金白银的资源,Redis 的设计思路和其他数据库不太一样。它不追求所有数据都放在内存里慢慢淘汰,而是默认给你一套“有限内存下的最佳实践”:热点数据常驻,冷数据按策略淘汰,持久化只是兜底,而不是主路径。看 REPL 的那个七十年代简洁设计,但实际上你要真正用好 Redis,就需要按照“有限内存 + 高性能 + 可退化”这个思路去看待它,而不是把它当成 MySQL 的替代品。
1.2 哪些场景才是 Redis 的主场
如果你的系统里出现下面这些情况,基本可以考虑引入 Redis:第一,热点数据读多写少,比如商品详情、用户资料、配置项,数据库扛不住高并发查询时用它做缓存;第二,需要临时计数的场景,比如文章的浏览量、直播间在线人数,自增自减操作非常合适;第三,跨多实例或跨服务协调,比如分布式锁、幂等控制、限流计数;第四,需要相对快速的排行榜,有序集合天然支持按分数排序;第五,发布订阅和简单消息队列,比如实时通知推送、异步任务触发。
反过来说,Redis 不适合做多表关联查询,不适合存超大个别的普通消息流,也不适合做严格的强一致性存储。经常有人在生产里把几十 GB 的数据一股脑塞进 Redis,然后发现内存紧张、持久化卡顿,这其实是使用姿势错了,不是 Redis 本身不行。
2. 数据类型与底层实现:你操作的不只是 KV
2.1 五种基本数据类型和它们最常用的用法
很多人面试或被问 Redis 数据类型,都背过:String、Hash、List、Set、Sorted Set。但工作中真正重要的是知道它们在什么场景下用什么。String 是最常见的通用缓存结构,能存字符串、数字、二进制内容,后面自带的 INCR/DECR 可以在并发计数场景里原子操作,不必担心超发或重复扣减。Hash 适合存对象,比如一条用户记录用 key 存用户 ID,field 存用户名、积分、等级,只更新某单字段时比整体序列化写入要更省时间和流量。List 是双向链表或者紧凑列表,适合做简单消息队列、时间线分页,也能配合 BRPOP 实现阻塞读取。Set 是无序集合,天然可以去重,适合做共同好友、标签聚合。Sorted Set 则是排行榜场景的首选,每个成员带 score,ZADD/ZREVRANGE 一下就能拿到前 N 名。
实际使用中要特别留意 key 的粒度。有人习惯把“用户:1001:订单”拆成几十个 key,结果内存翻倍、过期管理乱七八糟;也有人把所有业务数据都放进一个 hash,结果单 key 巨大,后续扩容和淘汰都很难处理。我的建议是一个业务对象尽量一个 key,字段级功能交给 hash,如果发现需要跨 key 做关联操作,先退一步想想是不是模型设计错了。
2.2 底层编码和内存效率的秘密
Redis 在设计编码时花了很多心思,同一个 key 在不同数据量和元素大小下,实际存储结构可能是不同的。Redis 7.0 之前,List、Hash、Sorted Set 在满足“元素很少、值很小”条件时会采用紧凑编码,比如 ziplist;7.0 后这部分逐渐被 listpack 替代。目标都一样:减少内存碎片和指针开销。当你不断向这些结构里追加写入数据,超过阈值后 Redis 会把它转换为标准编码,比如 Hash 变成 hashtable,Zset 变成 skiplist,List 变成 quicklist。
这个设计带来的实际影响是:写大量小 key 时内存占用很低,但一旦元素量增长或单个元素超过阈值,结构转换是隐式的,可能导致内存突然上涨和 CPU 短暂波动。如果线上观察到 Redis 内存突然跳变,除了数据和 key 量增长,还应该想到是不是某个大对象触发了编码转换。判断一个 key 用什么编码,用 OBJECT ENCODING key 就能直接查出来,排查问题非常有用。
2.3 跳表这个结构为什么被用在有序集合里
要理解有序集合底层的 skiplist,可以把它想象成一个分层的“快速通道”链表:最底层是完整的有序链表,上面几层只保留部分节点作为“跳过”用的索引。查找时从最高层开始,如果当前节点下一个值小于目标就继续往前跳,大于目标就下沉到下一层继续走,平均复杂度是 O(log n),最坏情况也不至于太差,而且实现比平衡树容易调试。
Redis 选择 skiplist 而不是红黑树,据说一个重要原因是区间查找在 skiplist 上很自然,顺序遍历直接沿着底层链表走就行。再加上它还配了一张哈希表,用于 O(1) 精确定位 member,于是有序集合既能快速按分数取区间,也能快速查单个成员的分数。这套“哈希表 + 跳表”的组合是 Redis 里很典型的设计:不单纯追求某一种操作的极致,而是让各类操作都有相对稳定的表现。
3. 持久化机制:数据安全与重启恢复
3.1 RDB 快照:全量备份的省心方案
RDB 是在指定时间点把内存里全量数据生成一份压缩的二进制文件。触发方式有多重:配置里的 save 规则、手动执行 BGSAVE、或者关闭服务时自动保存。执行 BGSAVE 时,Redis 会 fork 出一个子进程,子进程把数据写入临时 RDB 文件,写完后原子替换旧文件。父进程继续处理客户端请求,不需要阻塞主线程。RDB 的优点在于文件紧凑、恢复速度快,特别适合做冷备和灾难恢复,也适合在集群间作为初始全量同步数据。
需要注意“fork”没有那么轻。如果内存里大量数据、机器内存又紧张,fork 瞬间可能消耗大量内存,甚至在极端情况造成操作延迟抖动。生产里一般把 auto-compact 配置的触发频率控制住,避免频繁的大 RDB 保存;同时建议通过 slowlog 和延迟监控观察 fork 耗时。我踩过一个坑:某台老机器内存只剩 1GB 空间,Redis 又分配了 6GB,BGSAVE 时操作系统开始 SWAP,服务延迟直接飙到秒级。后来就是硬性保证“RSS 不超过系统总内存的一半”才彻底解决。
3.2 AOF 日志:写命令的追加日志
AOF 记录的是每个写操作的命令,类似数据库的 binlog。默认每写完一条命令就会调用 fsync 吗?不一定。配置里面的 appendfsync 有三个选择:always 表示每条指令都调用 fsync,文件最安全,但吞吐会明显降低;everysec 是每秒刷一次盘,兼顾性能和安全性,也是生产环境里最常见的选择;no 则交个操作系统决定,性能最好但异常丢失数据的窗口最大。顺着项目对数据安全的要求去选,很多业务接受每秒丢一点,用 everysec 就够;如果一条都不能丢,那建议用 always,并做好性能评估。
AOF 文件会持续增大,Redis 提供了重写机制 AOF rewrite,会以重写时的内存快照为基础生成新的最小命令集。触发条件通常由 auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size 控制。重写过程同样会 fork 子进程,但父进程同时把新写入命令缓存起来,重写完成后会合并这些缓存再替换 AOF 文件,这个过程在 Redis 7.0 之后的 multi-part AOF 机制里更精细化,减少了大文件切换时的风险。生产里不应该让 AOF 无限增长下去,也不应该完全依赖自动策略;必要时应在业务低峰期手动执行 BGREWRITEAOF。
3.3 混合持久化与我的备份习惯
Redis 4.0 之后提供了混合持久化能力,默认开启。简单说,AOF 重写后的文件里先有一份 RDB 格式的全量数据,后面再追加一段 AOF 格式的增量命令。重启恢复时顺序会非常快:加载 RDB 部分直接还原内存,再 replay 一段增量命令,比纯 AOF 重放全量命令快得多,比纯 RDB 又不容易丢数据。这也是目前主流部署下比较稳妥的方案。
但持久化只是兜底,不是一切。我自己的备份纪律是两条:第一,永远把 RDB/AOF 文件从 Redis 节点复制到独立存储或者对象存储里,不能只依赖本地磁盘,否则机器坏了什么都完了;第二,定期做一次“恢复演练”,拿备份文件在一个干净环境里启动 Redis,确认数据能正常加载,节点能正常对外服务。很多团队灾难恢复流程看起来完整,一演练才发现 Redis 版本的 RDB 格式不兼容或文件权限丢失,这种事发生过太多次。
4. 生产环境搭建:从安装到可视化连接
4.1 Redis 下载与安装的几种姿势
先说系统环境。Linux 上最省心的方式是直接通过包管理器安装或使用官方源码编译,生产环境我更多会用源码编译,原因是能精确控制版本和编译参数,不会突然被系统升级换掉。官方源码包从 redis.io/download 下载,对应版本的 SHA256 要和官网核对。Redis 6.2 和 7.0 以上在稳定性和新特性上有差异,现阶段的建议是:新项目直接用 7.2 系列,老项目如果已经稳定运行在 6.2,不必强行升级,但要关注安全补丁。
Windows 下要特别提醒:Redis 官方对原生 Windows 的支持非常有限,很多人在网上找到的“windows 版本 redis 下载”都是第三方编译版。如果是本地开发调试,用第三方版本或 Redis Desktop Manager 这类工具连过去的做法问题不大,但生产服务器请优先使用 Linux,别再问为什么 Windows 上主从切换老出诡异问题了。如果你必须习惯在 Windows 上做测试,强烈的建议是在 WSL2 里起一个 Linux 环境,再跑 Redis,这样和生产行为更接近,同时可以照常使用 Docker。
4.2 Docker 安装与 Compose 生产环境部署
Docker 方式确实是团队协作最快的方式。官方镜像可以在仓库里直接下载 redis:7.2-alpine,相比标准版它更小,也不带不必要的包。开发环境一条命令就能跑起来:
docker run -d --name redis-dev \ -p 6379:6379 \ redis:7.2-alpine生产环境不建议这样裸跑,至少需要加上持久化目录、密码认证、内存限制和日志饮食。下面是我常用的 Compose 模板:
version: "3.8" services: redis: image: redis:7.2-alpine container_name: redis restart: always command: - redis-server - --requirepass ${REDIS_PASSWORD} - --appendonly yes - --appendfsync everysec - --maxmemory ${REDIS_MAXMEMORY} - --maxmemory-policy allkeys-lru ports: - "6379:6379" volumes: - ./data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf deploy: resources: limits: memory: 2g这里重点解释几个参数的选择:requirepass 是防攻击的基本要求,公网环境下没密码的 Redis 基本等于裸奔;appendonly yes 保证至少能容忍秒级数据丢失;maxmemory 给实例设一堵“墙”,防止 Redis 内存无限上涨拖垮宿主机;maxmemory-policy 则控制达到内存上限后的淘汰策略,后面我会单独展开。另外,建议始终把 redis.conf 通过 volume 挂进去,让你的配置可审查、可版本控制,不至于在容器重启后所有参数都回到默认。
4.3 可视化工具与客户端选择
命令行 redis-cli 永远是最可靠的排障工具,但日常开发中我还是会配一个可视化客户端。早期常用的 Redis Desktop Manager 确实好用,只是后来部分版本和许可证变化,很多团队又在寻找替代品。我目前用得比较多的是 Another Redis Desktop Manager,这个东西开源免费,跨平台,支持 Mac/Windows/Linux,也能够显示 key 的 TTL、查看内存分析、执行命令以及管理多个连接,对于日常开发调试足够了。
连接 Redis 时有个细节:先把连通性测试好,再去看数据。用客户端连接失败,90% 是因为没关闭 Linux 防火墙、Redis 的 bind 配置只监听 127.0.0.1,或者 protected-mode 处于开启状态。生产环境不要为了图省事把 protected-mode 设成 no 然后直接暴露公网,这是很危险的做法。正确的姿势是:绑定内网网卡,开启密码,最后加一台跳板机或者通过安全组限制来源 IP。
5. 进阶实战:分布式锁、缓存治理与序列化
5.1 分布式锁的正确使用方式
在微服务架构里,多个服务实例同时操作一份数据时,通常需要分布式锁。Redis 的分布式锁本质是利用单线程处理命令,配合 Set 指令要做到原子性。很多人最初的写法是先用 SETNX 再设置过期时间,结果中途进程崩溃,锁永远不释放。这个问题可以在一条命令里解决:
SET lock:order:10001 token_value NX PX 30000NX 表示只有 key 不存在时才设置,PX 表示过期时间,单位和业务执行时间要仔细估算。释放锁时不能直接 DEL,否则可能把别人刚拿到的锁删掉,因为锁还带着一个唯一标识,释放脚本要用 Lua 保证“先比较后删除”这一步是原子的:
if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end实际项目中我建议直接使用 Redisson 这类成熟库提供的 Lock 工具,它们内置了看门狗续期机制,避免业务代码执行时间超过锁过期时间导致锁被提前释放的问题。但无论用不用 Redisson,原理层面至少要理解:锁的互斥靠 key set,锁的自动释放靠 expire,锁的防误删靠唯一标识,这三点缺一不可。如果业务允许只依赖单点 Redis 做锁,就不要强行搞 RedLock,那套跨节点一致性方案在很多团队场景里没必要,反而增大复杂度。
5.2 缓存治理:命中率、TTL 与数据一致性
缓存不是“加了 Redis 就完事”,核心指标是命中率、数据和的一致性。高命中率意味着数据库压力确实被分担了;TTL 设置如果过短,大部分请求永远访问不到缓存,等于白装 Redis;TTL 过长,数据更新后缓存和数据库长期不一致。常规经验:读多写少的配置类数据,TTL 可以设置到数小时甚至一天;热点商品详情,适当随机化 TTL,避免同一时间大量击穿;即使改了数据,也建议用“先更新数据库,再删缓存”而不是“先写缓存”,原因在于删缓存可以规避并发写库和缓存覆盖的问题。
数据一致性是个系统工程。我先说结论:缓存二话不说直接删,不要抱着“缓存也更新一下”的想法。因为缓存更新很容易因并发覆盖导致脏数据,而删除缓存后下一次请求读到旧值最多触发一次回源。配合 TTL 和消息队列做最终一致,基本能覆盖绝大多数业务场景。如果对强一致有要求,那就要从应用架构层面重新考虑,而不是依赖缓存层来解决。
5.3 Redis 序列化别踩这些坑
Java 和 Redis 交互时,序列化方式是很多团队踩坑的重灾区。使用 Spring Data Redis 时,如果直接用 JDK 序列化,Redis 里看到的 key 会带一堆二进制前缀,肉眼根本分不清是什么;而且 JDK 序列化出的对象体积膨胀、反序列化时如果实体类结构变化,又容易直接抛异常。另一个常见问题是某些库默认用 JdkSerializationRedisSerializer,导致你通过可视化工具想找 key 时,看到的不是“user:1001”而是“\xAC\xED\x00\x05t...”。
我这里给一套稳妥组合:key 统一用 StringRedisSerializer,不用把二进制曝光;业务对象用 GenericJackson2JsonRedisSerializer 或者手动转换成 JSON,前提是类需要有无参构造和 getter/setter,否则反序列化时会很头疼。对于高吞吐但结构非常固定的数据,也可以考虑 Protocol Buffers,只不过有点复杂化了。总之学会想法是:Redis 里存的可读性、体积、兼容性,三者要一起权衡,别选了一个伤害另外两个。
6. 高可用架构:主从、哨兵与集群
6.1 主从复制搭建的详细步骤
单机 Redis 一旦宕机,缓存全部消失,数据库瞬间受到请求风暴冲击,所以生产环境至少要有主从架构。主从配置的格式在 5.0 后是 replicaof,老版本叫 slaveof,含义一样。搭建步骤很简单:在从节点配置文件里设置主节点的 IP 和端口,同步启动后会从主节点做全量复制,之后增量复制持续同步。全量复制依靠 RDB 文件完成,从节点先收到 RDB 并加载到内存中,再继续处理主节点同步来的缓冲区命令。
如果你用 Docker 部署主从,可以使用 compose 一次性拉起两个节点。这里给一个最小示例:
services: redis-master: image: redis:7.2-alpine command: redis-server --port 6379 --requirepass masterpass redis-replica: image: redis:7.2-alpine command: redis-server --port 6380 --replicaof redis-master 6379 --masterauth masterpass depends_on: - redis-master主从复制有几点需要特别注意:主节点启动时如果没开启持久化,重启后是空库,从节点会照单全收,把从库也变成空库,所以主节点必须配置持久化;从节点默认是只读的,如果误写入从节点,不会同步给主节点,还会污染数据;复制断线后如果主节点积压的缓冲太小,会触发全量重新同步,网络不稳定时会有明显延迟。生产监控里要重点看 INFO replication 里的 master_repl_offset 和 slave_repl_offset,双方相差过大,说明从节点同步远远落后,查询从库可能会读到旧数据。
6.2 哨兵模式:自动故障转移是怎么做到的
主从复制只能保证数据有冗余,不能保证主节点挂掉后自动恢复。哨兵模式是为了做自动故障转移:哨兵进程监控主从节点的状态,发现主节点客观下线后,会在从节点里筛选一个新主,并让其他从节点重新指向新主。配置哨兵时最常用的一条是:
sentinel monitor mymaster 127.0.0.1 6379 2monitor 后面的“2”表示至少两个哨兵同意主节点不可用,才会触发故障转移。生产里哨兵数量至少要 3 个且部署在不同机器上,否则哨兵自己挂掉或者网络分区时,整个系统反而更容易出问题。
有人会遇到“redis 哨兵模式启动未生成 known”的问题,在网上一搜经常看到。其实哨兵启动后会周期性从主节点获取从节点信息和其它哨兵信息,并打印类似 known-replica 或者 known-sentinel 的内容,但这个感知不是瞬时的,通常要等几秒到十几秒。如果你等了很久还是没有生成,重点检查几件事:哨兵配置里的 master 名称是否和主节点配置一致;主节点是否设置了 requirepass,而哨兵配置里没有配 sentinel auth-pass;以及哨兵和各个从节点之间的网络是否互通。纯调试场景下,可以用 redis-cli -p 26379 登录哨兵,然后执行 SENTINEL master mymaster 和 SENTINEL replicas mymaster,观察哨兵到底看到了什么状态。
6.3 集群模式:当单机内存与写并发扛不住时
Redis Cluster 是另一条升级路径,通过哈希槽把数据分布到多个节点上。整个集群固定有 16384 个槽,每个节点负责一部分,客户端根据 CRC16(key) % 16384 计算 key 应该落在哪个节点。集群的好处是不需要把数据复制到所有节点,每个节点只管一部分数据,写能力可以横向扩展。但对应的代价是:多 key 操作只有在它们,并且各 key 都属于相同哈希槽时才支持;否则要做跨节点操作会报错。生产项目在设计 key 时就该考虑把需要一起操作的 key 统一放在相同哈希槽里,比如用 hashtag 的方式。
集群搭建的坑也不少。第一,不能把所有节点放在一台机器上,那只是心理上的高可用;第二,集群内部除了 6379 端口,还需要 16379 这样的总线端口保持节点间通信,防火墙没放开,集群会一直抖动;第三,扩容时槽迁移会影响性能,尽量避免业务高峰期做 rehash;第四,数据一致性是最终一致,网络分片期间可能丢失写操作,所以对强一致要求高的字段还是要靠持久化和应用层保证。
7. 生产避坑实战:日志、排查与参数调优
7.1 用什么指标判断 Redis 状态是否健康
很多初级工程师遇到 Redis 卡顿,第一反应就是重启服务,但重启后往往旧问题又弹出来。真正定位问题要先看指标。INFO stats 里的 total_commands_processed、instantaneous_ops_per_sec 能告诉你当前 QPS;INFO memory 里的 used_memory_human、used_memory_rss、mem_fragmentation_ratio 能告诉你物理内存和逻辑内存的差异,比值过高意味着碎片或者 swap 问题。INFO replication 用来判断主从同步是否健康,INFO keyspace 用来看每个库的 key 数量和过期 key 数量。
慢查询是另一个排障入口。Redis 单线程执行命令,一条慢命令会阻塞所有后续命令。通过 SLOWLOG GET 10 能看到近期的慢命令,配合 CONFIG SET slowlog-log-slower-than 10000 把大于 10 毫秒的操作记录下来。最常见的慢命令是大范围 KEYS 匹配、大量元素的 SMEMBERS、超大 key 的 HGETALL 等。尽量不要在业务线上去跑 KEYS,要遍历 key 就用 SCAN 的游标方式分步进行,并在低峰期操作。
7.2 缓存穿透、缓存击穿和缓存雪崩的应对
这三个问题经常被放在一起说,但成因和策略完全不同。缓存穿透是指请求根本不存在的 key,Redis 没缓存,请求直接打到数据库,万一是恶意刷接口,数据库会瞬间被打爆。解决思路:第一,参数校验,对明显不合理的 key 直接返回;第二,对查不到的值也缓存一个空值并设置短 TTL;第三,用布隆过滤器预先拦截不存在的键,这个方案在数据量极大的场景下效果更明显。
缓存击穿指的是某个热点 key 过期,同时大量请求涌进来,都去数据库回源,数据库压力瞬时翻倍。应对方式是:不让热点 key 同时过期,过期时间加一个随机量;更保险的做法是用互斥锁,在缓存重建期间只允许一个线程查库和回填,其他线程等待缓存重建完成后再读取。这里本质上是 spring 和高并发场景里常见的 single-flight 模式,能用框架的尽量别自己造。
缓存雪崩是大量 key 在同一时间过期,或者 Redis 整机不可用,导致所有请求直接冲向后端。前者靠时间随机化解决,后者则要依赖高可用架构(主从 + 哨兵)和服务降级限流。所谓降级,不是说 Redis 挂了就直接 fail,而是后端访问尽量动态化:没有缓存就返回默认值或空列表,避免把所有负载都压到数据库上,同时把关键业务的核心链路保护住。
7.3 内存淘汰策略:当 Redis 内存不够时会发生什么
Redis 在达到 maxmemory 后会根据 maxmemory-policy 做处理。默认策略是 noeviction,也就是不淘汰任何数据,直接返回 OOM 错误。这个策略对数据库这种不能丢数据的场景是安全的选择,但如果你把它当纯缓存用,就会非常尴尬:Redis 内存满了之后写不进去,新数据全部失败,进而影响业务。所以纯缓存场景一般用 allkeys-lru,最近最少使用的老数据会被优先淘汰,新数据可以正常写入。
另外一种常见的 allkeys-lfu 是根据访问频率淘汰,适合那些希望低频冷数据尽快被清掉的场景。如果你给不同 key 设置了不同的过期时间,可以选 volatile-ttl,优先淘汰即将过期的 key。但注意这些策略都是牺牲一定数据可用性换取写入能力的策略,核心业务数据不应该靠缓存淘汰来托底。生产上应该把 Redis 内存量、淘汰次数和淘汰 key 数都纳入监控,当淘汰量显著增大时,就说明容量规划出了问题,早点扩容或者做集群拆分,而不是等缓存命中率掉到惨不忍睹再补救。
7.4 日志和常见诡异问题排查记录
Redis 日志位置由 logfile 参数决定,生产建议打开并且按天切割,不然排查问题时日志文件大得打不开。设置 loglevel warning 和 notice 都行,线上不要用 debug,调试完记得改回来。常见诡异问题我整理了几个:主从切换后客户端抛 no reachable node in cluster,多半是客户端没有配置完整节点列表或集群总线端口不通;明明设置了 expire,key 还一直存在,可能是 EAXPIRE 过期时间单位有的是秒有的是毫秒,搞混;AOF 重写后磁盘满了,Redis 启动不了,这时候千万别乱删 AOF,先把磁盘清理到安全水位再启动。
还有一个容易被忽视的:Redis 对内存碎片和 swap 极其敏感。用 free -m 看到 swap 有占用时,基本可以确定 Redis 性能已经受影响了,因为内存页面被换出磁盘后,任何访问都会触发磁盘 I/O。解决办法只有释放内存、加物理内存或者迁移到资源更充足的实例上。总之,Redis 是一个看起来“轻”但实际对资源和运维方法要求极高的组件,宁可前期多花一点时间配置监控和演练故障恢复,也别等线上出了故障再来补救。
最后再分享一点个人体会:不要为了用 Redis 而用 Redis,每个功能引入之前先问一句“没有它还成不成立”。检查项目里引入 Redis 的动机,如果只是为了缓存一个一分钟就变的接口,并且数据库本身也能轻松扛住并发,加点 memory 也不一定是加分项;反而是真正的热点数据、高并发写、多实例协调这些场景,Redis 的价值才真正被放大。平时多看点官方文档和 release notes,自己动手搭一次从下载、配置、主从到哨兵的整套流程,比背一百道面试题都管用。