
Redis 这玩意我第一次接触还是因为线上接口慢到被业务方打电话催当时第一反应就是查数据库索引结果索引没问题单纯就是热点数据把数据库连接打满了。后来把一批热门数据挪进 Redis接口直接从 300ms 干到 10ms 以内那会儿才知道什么叫“缓存中间件”。后面几年从单机缓存一路折腾到主从、哨兵、Cluster再踩遍分布式锁和持久化恢复的坑算是彻底把 Redis 的脾气摸清了。这篇不整官方文档那套按我真实落地顺序来先搞懂它到底解决什么问题再动手安装配好然后逐个讲数据类型、缓存治理、持久化、分布式锁、高可用集群这些硬核场景最后把连接超时、大 Key、慢日志这些实战排查经验也一并分享。不管你是刚接触 Redis 的新手还是已经在生产环境维护 Redis 实例的开发这篇差不多能让你把高频知识点和实操坑一次带走。1. Redis到底解决了什么问题先想明白再上手1.1 从“数据库太慢”这一件事说起很多新手一开始就说“我要给项目加 Redis”但问他为什么加回答通常是“大家都用了肯定有用”。这种用法最危险因为方向错了后面全是补救。Redis 最核心的价值就一句话把频繁读取的数据从磁盘挪到内存把几十毫秒的查询降成微秒级返回。举个例子你一个商城首页要展示热销商品列表如果每次请求都从 MySQL 里查QPS 高了以后数据库连接被占满响应时间直线上升。但如果你把热销商品列表序列化后放进 RedisKey 设计成product:hot:list缓存命中时直接从内存返回结果不碰数据库。压测数据我实测过单机 MySQL 极限状态也就几千 QPS而同一台机器上的 Redis 单实例读性能能到 10万 QPS差了不止一个量级。但注意Redis 不是用来存全量数据的内存贵是现实问题。它解决的是“读多写少、热点集中、实时性要求高”这类数据场景比如会话信息、商品详情、排行榜、验证码、接口频率限制、分布式锁等。理解这一点你就不会拿它当万能存储乱塞数据。1.2 数据结构才是它真正的护城河很多人拿 Redis 和 Memcached 对比觉得都是内存缓存有啥区别。最本质的区别就是数据结构。Memcached 只能存key-value你所有的复杂度都在应用层解决而 Redis 自带五大数据类型每一种都对应一类典型问题。简单列下使用心法而不是八股文String最通用的缓存形态也适合做计数器、分布式 ID、验证码因为 Redis 的 INCR/DECR 是原子操作。Hash适合存对象比如用户信息、商品信息可以只修改其中一个字段不用整个对象反序列化再写进去。List支持双端操作天然适合做简单消息队列、最新消息列表。Set自动去重还能做并集、交集、差集适合做标签系统、共同好友这类场景。ZSet带权重排序的集合是排行榜、延时队列的最佳拍档也能用 score 存储时间戳实现定时任务扫描。初学阶段我建议把每个类型至少用一个真实案例串起来死记命令背得快忘得也快但如果你能说明白“为什么这个场景用这个结构”面试和实战都不太会翻车。1.3 部署形态的选择单机、主从、哨兵、Cluster这个阶段很容易犯的错是“先装个单机再说”。单机 Redis 确实能跑但它同时意味着单点故障内存挂掉、进程挂了、机器重启了缓存直接清零。你要评估业务容忍度再决定架构。我给一个比较务实的选型建议并发量不大、缓存可丢失、运维能力一般可以用单机 Redis但一定开持久化。需要高可用、读多写少做主从复制写走主、读走从主挂了还能手动提升从节点。需要自动故障转移加哨兵Sentinel监控主节点状态主挂了自动选新的主。数据量很大或写并发高到单机撑不住上 Cluster 集群把数据通过哈希槽分散到多个节点。在我维护过的业务里大部分项目其实到“主从哨兵”就够了直接上 Cluster 反而因为跨节点的复杂操作和客户端字段限制带来不少额外负担。这个决策后面专门讲集群的时候再展开。2. 手把手把 Redis 装起来Windows、macOS、Linux 一次说清2.1 Windows 下安装免安装解压版最省心Redis 官方并不直接提供 Windows 版本但开源社区有维护编译好的 Windows 版本常见的有 5.0.14.1 这类镜像版本。安装方式推荐直接下载 zip 免安装包解压后目录里有redis-server.exe和redis-cli.exe比安装包更干净。启动方式很简单命令行进到目录后执行redis-server.exe redis.windows.conf然后另开一个窗口测试redis-cli.exe -p 6379 127.0.0.1:6379 ping PONG能返回 PONG 就说明服务已经正常跑起来了。我习惯把 Redis 注册成 Windows 服务避免每次开机手动开窗口redis-server.exe --service-install redis.windows.conf --service-name RedisService redis-server.exe --service-start --service-name RedisService注意 Windows 版本的 Redis 更新普遍滞后于官方版本所以生产环境如果是 Linux 服务器别用 Windows 版本跑仅适合本地开发调试。2.2 macOS 下用 Homebrew 一装到底macOS 上安装推荐用 Homebrew简单到没什么技术含量brew install redis安装完成后可以先用前台方式验证redis-server /opt/homebrew/etc/redis.conf如果希望开机自启用 brew servicesbrew services start redisHomebrew 的配置文件默认在/opt/homebrew/etc/redis.confApple Silicon 路径或/usr/local/etc/redis.confIntel 版本需要改密码、持久化参数时就去这里改改完重启服务生效。2.3 Linux 上通过 Docker 快速部署Linux 上最省事的方式是用 Docker尤其适合测试环境快速拉起一个实例docker run -d --name redis \ -p 6379:6379 \ -v /data/redis:/data \ redis:7.0 redis-server --appendonly yes这里把容器的/data目录挂载到宿主机开启 AOF 持久化后即使容器删掉重建数据也还在。生产环境我会用 docker-compose 管理把端口、密码、持久化配置都固化在文件里方便团队复用。不过有一点要提醒Docker 跑 Redis 在 I/O 密集场景下磁盘持久化性能比裸机部署有损耗尤其是 AOF 重写时频繁 fsync 可能会引起阻塞。追求极致性能的生产环境我更建议用二进制方式直接部署在物理机或虚机上Docker 更多用于测试、CI、临时环境。2.4 配置密码和访问控制别裸奔上线Redis 默认无密码且默认绑定本机127.0.0.1。你在本地开发无所谓但一旦部署到服务器如果protected-mode没关且没设密码公网扫到直接就能连进来可以刷数据、写定时任务、植入挖矿程序。我见过不止一次有人 Redis 被入侵就是因为裸奔。设置密码的两种方式运行时动态设置仅临时生效127.0.0.1:6379 CONFIG SET requirepass your-strong-password要永久生效在配置文件中加一行requirepass your-strong-password同时建议修改默认端口别用 6379可以改成五位数高位端口降低被扫描到的概率。更安全的做法是开启 ACL给不同业务配不同的用户权限ACL SETUSER appuser on app_password ~cache:* read write -admin这样一个业务一个账号权限只开到它需要的那部分 key 前缀上出问题也能定位到是谁在误操作。3. 五种核心数据类型与高频命令实战3.1 String不止能存字符串还能做计数器String 是 Redis 里最基础的类型SET key value、GET key就是它的日常形态。但它真正值钱的特性是原子自增自减也就是 INCR、DECR、INCRBY、DECRBY 这些命令。比如实现一个接口限流用户每分钟最多调用 10 次可以用过期时间和自增配合127.0.0.1:6379 SET user:123:limit 0 EX 60 NX 127.0.0.1:6379 INCR user:123:limit第一次设置时用EX 60指定 60 秒过期NX保证不存在时才设置之后每次请求 INCR 一次当返回结果大于 10 时拒绝请求。这套方案实现限流非常轻量而且 INCR 原子性不用加锁极端并发下也不会计数错乱。做分布式 ID 也是类似思路用 INCR 生成全局唯一单调递增编号比数据库自增 ID 更抗压不过要注意 Redis 持久化出问题时有 ID 回退的隐患所以对绝对严谨的 ID 场景要谨慎。3.2 Hash对象存储的正确打开方式Hash 类型相当于一个 key 对应多个 field-value适合存对象。例如用户信息HMSET user:1001 name zhangsan age 28 city beijing HGET user:1001 name HGETALL user:1001如果换成 String 存通常就两种做法要么把整个对象序列化成 JSON 作为 value要么拆成多个 key。JSON 方案的问题是改一个字段要整体读改写大对象频繁更新很亏多 key 方案的问题是 key 数量爆炸且不利于管理。用 Hash 就舒服很多可以单独更新某个 field内存消耗也比 JSON 小。但 Hash 也有坑field 不支持单独设置过期时间只有整个 key 能设 EXPIRE。所以如果你需要“用户 30 分钟不活跃就删除 session”这种细颗粒度过期Hash 不太好实现要么整体过期要么靠应用层清理。3.3 List消息队列和最新列表都能干List 是双向链表可以从左边 LPUSH右边 RPOP实现先进先出LPUSH task:queue {json data} RPOP task:queue更推荐用阻塞版本BRPOP task:queue 0当队列为空时客户端挂起等待而不是轮询空转。这比数据库表做任务队列省心很多也避免了空查询打爆数据库的问题。我早期做异步任务的时候就是用BRPOP实现了简单的生产者消费者模型代码量极小。List 的另一个经典场景是存最近列表。比如用户最新浏览记录用LPUSH写入再用LTRIM key 0 99裁掉超过 100 条的数据列表永远只保留最近 100 条非常高效。但要注意List 中间插入、按索引取值的操作是 O(N)数据量大了以后要避免高频使用LINDEX这类命令。3.4 Set 与 ZSet去重、标签、排行榜一次搞定Set 适合做自动去重的集合比如用户标签SADD user:1001:tags java redis mysql SISMEMBER user:1001:tags redis // 判断是否存在不同用户之间的共同标签用交集推荐关注列表用并集差异用户用差集这些都是 Set 的原生操作在应用层写起来非常绕的需求Redis 一条命令就出来了。ZSet 是带分数的有序集合每个成员跟一个 double 类型的 score按 score 排序。排行榜是最常用的场景ZADD leaderboard:game1 100 playerA ZADD leaderboard:game1 90 playerB ZREVRANGE leaderboard:game1 0 9 WITHSCORES这段命令就是取排行榜前 10 名按分数从高到低。ZSet 还能用 score 存时间戳实现延时队列启动一个定时任务去ZRANGEBYSCORE key 0 now把到期的任务取出来消费即可。这个思路比轮询数据库高效很多。3.5 综合小案例实现一个热门商品排行榜把上面这些串起来写一个简单但完整的热门商品榜。商品每次被点击时用 ZSet 累加热度ZINCRBY product:hot:rank 1 product:101 ZINCRBY product:hot:rank 1 product:102页面加载时取热度前 20 名的商品 ID再回查数据库补齐名称、价格、图片。为了防止榜单无限膨胀定期清理过期商品可以用ZREMRANGEBYSCORE product:hot:rank 0 min_score把低热度商品移除掉。这里面 ZINCRBY 是原子操作并发点击不会丢失计数。如果还需要“过去 7 天热门榜”可以按天拆分 key比如product:hot:rank:20250120最后对不同日期的有序集合做 ZUNIONSTORE 聚合就是时间段维度排行榜。这个案例做出来你对 Redis 的熟练度基本就超过大部分“会用 SET/GET”的人了。4. 缓存治理穿透、击穿、雪崩一次讲透4.1 缓存穿透查一个根本不存在的数据打爆数据库缓存穿透的意思是请求的数据在缓存和数据库里都不存在于是请求直接落到数据库。恶意攻击者可以故意请求不存在的用户 ID如果每次都穿透到数据库数据库压力会很大。解决方案有三个层次第一层接口参数校验。非法的参数直接拦截掉别往下走。第二层缓存空值。如果数据库查不到就往 Redis 写一个空值并设置较短的过期时间比如 60 秒避免同一 key 反复穿透。第三层布隆过滤器。把所有可能存在的 ID 先放进布隆过滤器请求进来先判断 ID 在不在过滤器里不在就直接返回连缓存都不用查。布隆过滤器的原理值得多说两句。它用多个哈希函数把元素映射成位数组上的多个点判断一个元素“一定不存在”很准确但判断“存在”有小概率误判因为不同元素可能把位数组上的位置重叠了。Redis 4.0 之后提供了 RedisBloom 模块可以用 BF.ADD 和 BF.EXISTS 来操作。设置合理的容量和误判率很关键容量太小会导致误判率快速上升。4.2 缓存击穿热点 key 过期的一瞬间请求全打到数据库缓存击穿和穿透非常像但击穿针对的是“缓存里有但正好过期”的某个热 key。这个 key 过期后大量并发请求瞬间涌到数据库相当于缓存防线出现了一个缺口。两个主流解法互斥锁。在缓存重建期间只允许一个线程去查数据库并回写其他线程等待。用 SETNX 实现互斥锁拿到锁的线程查库写缓存没拿到锁的线程 sleep 几十毫秒后重试读缓存。我一般设置 1 秒内重试多次避免线程长时间阻塞。这个方案实现简单能挡住绝大多数击穿场景缺点是存在加锁、等待的耗时而且要注意锁的过期时间防止线程宕掉后锁不释放。逻辑过期。就是不给缓存设物理过期时间而是在 value 中存一个业务过期时间戳。读取时发现逻辑过期后返回旧值同时异步用一个独立线程去重建缓存并更新过期时间。这种方式能扛住高并发且不会过度阻塞线程但实现复杂度高且返回的数据可能短暂过期。如果业务允许读取短暂旧数据这个方案更优雅。4.3 缓存雪崩大量 key 同时过期Redis 被击溃雪崩是击穿的扩大版。大量 key 集中在同一时间过期或者 Redis 实例宕机导致大批请求直接打到数据库。在大型活动场景里如果缓存设置了相同的过期时间就容易引发雪崩。我的处理习惯是过期时间加随机值。比如基础过期时间 10 分钟再叠加一个 0 到 300 秒的随机偏移量让 key 的过期时间在时间轴上散开。设置多级缓存。比如本地缓存 Redis 缓存Redis 挂了还能靠本地缓存扛一阵。用 Redis Cluster 或主从提高可用性避免单点宕机。数据库层做熔断降级超负荷时放弃非核心请求保证主流程可用。预防雪崩本质上靠的是“分散”而不是“硬扛”。过期时间打散、流量打散、压力分散这个思想贯穿所有缓存治理。4.4 缓存一致性先更新数据库还是先删缓存缓存一致性是面试几乎绕不开的问题。最经典的两个操作顺序要搞清楚。先更新数据库再删缓存是目前实践中最常用的方式。为什么不是先更新缓存因为并发场景下先写缓存很容易出现脏数据覆盖。删缓存的优势是等下次读请求再去数据库加载最新值成本低。但“先更库再删缓存”也有极端竞态问题线程 A 更新数据库线程 B 读取旧值并回写缓存然后线程 A 删除缓存此时缓存里留下的反而是旧数据。解决思路是延迟双删删除缓存后等待几百毫秒再删一次保证并发读线程回写的旧缓存被最终清掉。或者更可靠的方式是让缓存写入带上版本号或时间戳读请求回写时比较版本旧版本不写入。后者实现成本稍高但能从根本上避免脏写。按我现在的习惯一般用“先更新数据库再删除缓存”加“延迟双删”兜底并且对一致性要求极高的数据给缓存加一个比较短的过期时间作为底线兜底。5. 持久化机制RDB 和 AOF 到底该怎么选5.1 RDB 快照全量备份恢复快但可能丢数据RDB 是 Redis 把内存数据全量写入磁盘的文件快照。默认配置是save 3600 1表示 3600 秒内至少有 1 次写入才触发一次快照还有save 300 100300 秒内 100 次写入触发以及save 60 1000060 秒内 1 万次写入触发。配置是或的关系满足任意一条就执行。RDB 的优点是文件紧凑恢复速度快适合做冷备和灾难恢复。缺点是快照会丢失最后一次快照之后的新数据比如 59 秒内刚写入的数据突然宕机这部分数据就丢了。快照过程本身用 fork 子进程实现不影响主线程继续处理命令但如果数据量很大fork 瞬间可能因为复制内存页表导致短暂停顿这也是大实例要注意的点。5.2 AOF 日志记录每一次写操作最多丢 1 秒数据AOF 的工作方式是把每次写命令追加到日志文件末尾类似 MySQL 的 binlog。开启方式是配置appendonly yes。刷盘策略有 three 个appendfsync always每个命令都 fsync 到磁盘最安全但也最慢。appendfsync everysec每秒刷一次盘最多丢 1 秒数据性能和安全折中生产环境常用这个。appendfsync no由操作系统决定什么时候刷盘性能最高但丢数据风险也最大。AOF 文件会不断增长Redis 会通过bgrewriteaof进行重写把多条命令压缩成最终状态对应的最少命令数。比如对一个 key 做了 100 次 SET重写后就只剩最后一条 SET。AOF 的缺点也很明显文件比 RDB 大不少恢复时重放日志比加载 RDB 慢而且 fsync 策略配置不当会明显影响写性能。我记得第一次用 always 策略跑批量任务原本几毫秒的写请求直接变成几十毫秒后来果断改回 everysec。5.3 混合持久化两个都要鱼和熊掌兼得从 Redis 4.0 开始支持混合持久化。开启aof-use-rdb-preamble yes后AOF 文件开头会先写一段 RDB 格式的数据后续再追加命令。这样重启恢复时先加载 RDB 快速恢复大部分数据再重放少量 AOF 命令补全增量既快又少丢数据。现在的版本默认已经开启混合持久化。我的生产配置通常是appendonly yes appendfsync everysec aof-use-rdb-preamble yes同时保留 RDB因为 RDB 文件体积小适合定期备份到对象存储或者异地机房。这样“恢复快、丢数据少、备份方便”三个目标都能覆盖。5.4 数据恢复的实操过程与踩坑记录恢复数据时有两种典型路径。如果只有 RDB把 dump.rdb 放到配置指定的目录重启后自动加载。如果有 AOFRedis 启动时会优先加载 AOF 文件因为 AOF 包含的数据更完整。我踩过的一个经典坑是某次手动后台重启 Redis启动日志提示加载成功但数据少了最后一小段。排查发现是 AOF 文件损坏重启时 Redis 拒绝加载。此时如果用redis-check-aof --fix修复它会截断损坏的尾部命令但代价是丢掉尾部部分数据。所以关键教训是持久化文件必须定期做备份且备份要验证能否成功加载不要等到事故发生时才发现 RDB 文件已经损坏或残缺。还有一个细节RDB 触发非常频繁会影响性能比如把 save 参数调得过于激进。一般不建议为了“更安全”而把save 60 10000改成save 10 100因为频繁 fork 写盘会让 Redis 性能大幅下降。选一个合理的业务容忍度配合 AOF才是稳妥做法。5.5 数据安全的进一步建议关于数据安全我建议在配置层面做一个兜底开启 AOF用 everysec 刷盘。保留 RDB 做凌晨冷备备份文件至少保留最近 7 天。绝对不要在生产环境用CONFIG SET save 关掉持久化除非你只把 Redis 当临时缓存且能接受全部丢失。用SHUTDOWN SAVE正常关闭 Redis而不是直接 kill -9。虽然 AOF 下 kill 也不至于丢太多数据但优雅关机能保证 RDB 完整性。6. 分布式锁SET NX EX 就能实现但坑很多6.1 为什么需要分布式锁日常开发里多个 JVM 实例同时处理同一条订单数据比如同时执行扣减库存。JVM 内synchronized只能锁住当前进程其他实例照样并发执行结果就是超卖。分布式锁的本质是让多个进程通过一个公共组件Redis、ZooKeeper 等来协调保证同一时刻只有一个进程能执行关键操作。Redis 做分布式锁的核心命令就是 SET 的扩展参数SET lock:order:1001 unique_token NX EX 30NX表示只有当 key 不存在时才设置成功EX 30表示锁 30 秒后自动过期。如果设置成功说明拿到了锁逻辑处理完后删除锁释放。删除锁时一定要用 Lua 脚本校验持有者身份防止误删别人的锁。6.2 释放锁的细节别把别人的锁删了最典型的坑是线程 A 拿到锁执行时间超过了锁的过期时间锁自动释放线程 B 拿到锁开始执行。这时线程 A 执行完毕直接 DEL 删锁结果把线程 B 的锁删掉了。解决方式是设置锁的 value 为唯一标识比如 UUID删除前先 GET 判断是否属于自己的锁再用 Lua 脚本保证判断和删除的原子性。常见做法if redis.call(get,KEYS[1]) ARGV[1] then return redis.call(del,KEYS[1]) else return 0 end注意这个脚本必须用 EVAL 执行保证 check 和 del 两步之间不会被其他命令插入。但是即便这样还存在一个无解问题如果不做锁续期锁就可能在业务没结束前过期如果做了续期又增加了实现复杂度。这也是为什么很多人建议直接用 Redisson它的看门狗机制会自动续期。6.3 Redisson 与看门狗续期Redisson 是 Java 生态里最常用的 Redis 客户端之一它封装了完整的分布式锁 API并自带看门狗机制。简单说你拿锁时设置默认 30 秒过期Redisson 会启动一个后台线程每 10 秒检查一次如果锁还在且业务线程还活着就自动把过期时间续到 30 秒。业务执行完释放锁时续期线程也会被取消。用 Redisson 写分布式锁大概是这种感觉RLock lock redissonClient.getLock(lock:order:1001); boolean locked lock.tryLock(3, 30, TimeUnit.SECONDS); if (locked) { try { // 业务逻辑 } finally { lock.unlock(); } }tryLock 的参数里包含了等待时间、租约时间等。如果你用了看门狗不传 leaseTime 的话默认会自动续期。这套方案解决了我上面说的“锁过期导致业务冲突”和“误删锁”两个问题生产代码里强烈推荐。6.4 主从切换和 RedLock 的学术争论这里要提一个进阶话题Redis 主从架构下锁写入主节点后主节点还没同步到从节点就宕机了从节点被提升为主节点锁数据就丢了另一个线程就可能拿到同一把锁。对于严格不允许并发执行的场景这是个隐患。业界有 RedLock 方案向多个独立的 Redis 节点同时加锁超过半数节点加锁成功才认为锁生效。但 RedLock 在分布式系统领域一直有争议因为它依赖各个节点的时间假设在某些极端情况下依然可能失效。我的看法是如果业务对分布式锁的安全性要求极高首先要考虑是否应该引入 ZooKeeper 这类线性一致性更强的组件如果是在 Redis 体系内尽量使用全哨兵模式下的主节点加锁并在业务上做好幂等兜底。追求绝对安全只能靠多种机制叠加没有银弹。7. 高可用与集群从主从复制到 Cluster7.1 主从复制的基本原理主从复制解决了两个问题数据副本和高可用基础。主节点负责写从节点复制主节点的数据同时负责读流量。配置也很简单在从节点配置里加一行replicaof master-ip 6379或者运行时就执行REPLICAOF master-ip 6379。复制链路分为全量同步和增量同步主节点会把当前数据生成 RDB 快照传给从节点并缓存复制期间的新写命令从节点载入快照后再继续接收增量命令。主从存在的坑从节点如果因为网络闪断和主节点断开会自动重新连接并尝试增量同步但如果断连时间过长复制积压缓冲区里的新命令已经被覆盖就会退化成全量同步在大数据量下同步时间会比较久。所以积压缓冲区大小要结合网络稳定性和写入量来设置默认值太小在抖动频繁的环境会引发反复全量同步的问题。7.2 Docker 快速搭建一主一从用 Docker 搭建两个 Redis 实例做一主一从来验证原理非常方便。先创建自定义网络docker network create redis-net启动主节点docker run -d --name redis-master \ --network redis-net -p 6379:6379 \ redis:7.0 --requirepass masterpassword启动从节点并指定主节点docker run -d --name redis-slave \ --network redis-net -p 6380:6379 \ redis:7.0 --replicaof redis-master 6379 --masterauth masterpassword容器内用 redis-cli 验证docker exec -it redis-slave redis-cli -p 6379 INFO replication如果看到role:slave且master_link_status:up说明复制链路正常。这时候在主节点写数据从节点再 GET 就能查到。注意生产环境里从节点默认是只读模式如果要让从节点支持写操作必须显式配置replica-read-only no但我基本不建议这么做主从职责清晰比什么都重要。7.3 哨兵模式让故障转移自动化主从手动切换有个致命问题主节点宕机时如果没人手动提升从节点整个写链路就瘫痪了。哨兵Sentinel就是为了解决这个问题。它像一个监控进程不断给主节点发心跳发现主节点客观下线后会通过选举选出一个新的主节点然后把其他从节点重新指向新主。 选型上我会部署三个哨兵节点形成奇数个避免因网络分区出现脑裂。哨兵本身也是一个 Redis 进程用redis-sentinel sentinel.conf启动。核心配置有sentinel monitor mymaster master-ip 6379 2最后的数字 2 表示判断主节点客观下线至少需要 2 个哨兵同意。线上还有两个重要参数down-after-milliseconds表示多久没有响应就判定主观下线failover-timeout表示故障转移的超时时间需要根据网络情况合理调整太短容易转移失败太长会导致业务长时间不可用。实测下来哨兵本身就经历过一次脑裂场景最后决定把quorum严格设为哨兵数量的一半以上同时开启sentinel auth-pass mymaster password和主从密码防止无鉴权节点干扰选举。7.4 Redis Cluster数据分片与多主多从如果单台 Redis 内存和写入吞吐已经无法满足业务需要把数据分散到多个节点这时用 Cluster。它把数据空间划分成 16384 个哈希槽每个主节点负责一部分槽位客户端根据 CRC16 算法计算 key 所在的槽位路由到对应节点。Cluster 的最小推荐配置是 3 主 3 从。创建集群的流程一般是redis-cli --cluster create \ 10.0.0.1:7000 10.0.0.2:7000 10.0.0.3:7000 \ 10.0.0.4:7001 10.0.0.5:7001 10.0.0.6:7001 \ --cluster-replicas 1--cluster-replicas 1表示每个主节点配一个从节点。Cluster 模式需要注意的坑涉及多 key 的操作比如 MGET、跨 key Lua 脚本要求 key 必须在同一个槽可以通过 hash tag 实现比如把订单和用户 ID 都放到同一个{user:1001}前缀下。客户端要支持 Cluster 协议比如 Java 的 Redisson、Jedis 集群模式、Lettuce 集群模式普通单机客户端连集群会直接报 MOVED 错误。槽位迁移期间可能涉及较复杂的运维操作推荐配合 Redis 官方工具或可视化运维平台管理。如果你们的 Kubernetes 集群里已经比较成熟也可以直接用 Redis Operator 在 K8s 里创建 Cluster。它本质上是把多节点编排、配置、升级和健康检查全部自动化了比手动创建繁琐的集群省力很多。但底层原理还是这些所以我总建议先手动搭一遍再交给 Operator。7.5 K8s 中部署 Redis 集群的注意点K8s 部署 Redis Cluster 时首要问题是网络标识稳定性。Pod 重建后 IP 会变而 Redis 节点互相通信必须依赖稳定的域名或 IP。目前主流方案是通过 StatefulSet 创建带稳定网络标识的 Pod加上 Headless Service让每个节点通过redis-0.redis-headless.namespace.svc.cluster.local这样的域名互相发现。还要注意存储卷每个 Redis Pod 都要挂载独立的 PVC存储 AOF 和 RDB 文件不能多个节点共用一份存储。一个很常见的错误是把persistentVolumeReclaimPolicy设成 Delete结果删掉一个故障 Pod 时数据盘也被回收造成数据无法恢复。生产环境建议用 Retain 或者常规云平台默认策略至少保证手动恢复的可能。8. 可视化工具和日常排查8.1 可视化工具选型RedisInsight 和 Another Redis Desktop Manager命令行用多了还是想看图形的数据变化尤其排查 key 数量和内存分布的时候可视化工具能省很多事。我常用的工具有两个。RedisInsight 是 Redis 官方出的 GUI 工具界面清爽功能涵盖连接管理、浏览器、数据库指标、慢日志、命令行、内存分析等。支持 Windows、macOS、Linux下载安装后直接从界面配置连接即可。它对 Redis 7 的新命令支持最好我日常调试最新特性都会用它。Another Redis Desktop Manager 是开源社区维护的工具界面布局更适合习惯老牌 Redis Desktop Manager 的人它对连接树、多实例管理、内存分析都有支持。我之前用它排查过节点间 key 分布情况确实直观。连接工具使用的通用建议不要在生产环境直接连线上 Redis 乱执行危险命令先用INFO、DBSIZE、SCAN这些只读命令观察修改配置尽量走配置管理系统而不是用CONFIG SET临时改动。8.2 经典报错Command timed out 怎么排查我在本地开发时经常遇到Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException这是 Spring Boot 项目里很常见的报错。它的字面意思是 Lettuce 客户端在指定时间内没收到 Redis 的响应默认超时一般是 60 秒左右或者你配置的 spring.redis.timeout。出现这种报错我一般按顺序排查Redis 是否还活着redis-cli ping如果连通很快返回 PONG说明网络和进程层面没大问题。是否连接被耗尽执行INFO clients查看connected_clientsSpring 默认 Lettuce 连接池连接数通常在 8 到 16 之间如果被占满请求只能等待得超时。Redis 是否有慢命令阻塞用SLOWLOG GET查慢日志如果发现有KEYS *、HGETALL大 key、SORT这种耗时命令就解释得通了。是否是网络抖动检查客户端到 Redis 服务器的网络延迟观察报错是否是突发性、短时间恢复。如果用 Kubernetes 网络或云上安全组考虑是否丢包。有次生产事故就是因为一个大 Hash 的 HGETALL 动辄几秒直接把连接池占满所有请求都超时。后来做成字段拆分 分页读取问题立刻缓解。8.3 慢日志定位和 big key 扫描Redis 的执行是单线程的一个慢命令会阻塞所有后续命令所以“慢”在 Redis 里是大事。慢日志配置slowlog-log-slower-than 10000 slowlog-max-len 128上面的 10000 单位是微秒也就是 10 毫秒以上的命令会被记录slowlog-max-len限制最多保存多少条。用SLOWLOG GET 10就能看最近的慢命令。big key 扫描也用redis-cli --bigkeys扫一遍它会找出每个类型里最大的几个 key。注意这个命令在数据量大时会比较耗时建议在低峰期执行。处理 big key 的方法String 大 value 考虑压缩或拆分Hash 大 key 考虑按字段拆成多段List 大 key 考虑按时间或 ID 分段。另一个被忽视的排查项是内存碎片率INFO memory中的mem_fragmentation_ratio。如果这个值超过 1.5说明内存碎片比较严重通常和频繁的大 key 创建/删除有关可以考虑重启节点或调整 jemalloc 配置来整理碎片。8.4 Redis 日志怎么看别放过 warningsRedis 运行日志经常被忽略但它对定位问题很有用。重点看这几类内容WARNING Memory overcommit需要调整系统 vm.overcommit_memory1否则 RDB 持久化可能失败。WARNING The TCP backlog setting of 511 cannot be enforced高并发下需要调整内核 somaxconn。WARNING you have Transparent Huge Pages enabled建议关闭 THP避免 fork 期间触发内存大页导致的延迟波动。日志里频繁出现Loading RDB produced by version X.Y.Z说明每次启动都在加载 RDB如果数据量很大启动时间会很长可以考虑改用 AOF 并开启混合持久化缩短恢复时间。9. 高频面试题速查不只是背八股9.1 常考的理论问题面试聊 Redis高频理论题大概这些缓存穿透怎么解决、缓存击穿怎么解决、缓存雪崩怎么解决、Redis 为什么快、RDB 和 AOF 区别、Redis 单线程为什么还这么快、分布式锁怎么实现、主从复制原理、哨兵工作原理、Cluster 槽位机制。网上八股文很多但我发现最容易翻车的其实是“Redis 为什么快”这道题。常见的回答是“因为单线程 内存 IO 多路复用”但单线程本身并不代表快准确说法是数据在内存中读写、非阻塞 IO 多路复用、单线程避免了锁竞争和上下文切换开销配合高效的数据结构实现整体在绝大多数场景下能达到微秒级响应。你要能解释 Select/Epoll 模型为什么能在单线程下支撑高并发而不是生硬背模板。9.2 容易被追问的边界问题面试官喜欢顺着答案追问边界。比如问缓存一致性光说“先更新数据库再删缓存”不够要能说出延迟双删的原理、删除失败怎么办。我一般补充一条删除缓存失败可以通过订阅数据库 binlog异步重试删除这是比较完整的生产级方案。再比如问分布式锁光说 SET NX EX 容易被继续追问“锁过期了业务没执行完怎么办”这就要引出 Redisson 续期或者手动续期。答到这一层基本能看出你有没有真实生产经验。还有 Cluster 的坑为什么MGET在 Cluster 里可能报错因为多个 key 可能分散在不同槽。怎么解决用 hash tag。这种追问如果临时编答案很容易露馅。9.3 关于性能压测的实操建议面试里如果聊到性能建议你实际跑过压测而不是背数据。本地可以用 redis-benchmarkredis-benchmark -t set,get -n 100000 -c 50 -q这个命令用 50 个并发连接执行 10 万次 SET 和 GET输出 QPS 和延迟分布。我实测单机 Redis 7 在普通配置下 SET/GET QPS 能到 10 万以上Pipeline 批量提交更高。压测除了看 QPS还要关注 p99 延迟Redis 的 p99 延迟通常在零点几毫秒左右如果 p99 异常高大概率是触发了持久化、大数据 key 操作或网络抖动。9.4 一套自我检查清单最后分享一下我对 Redis 问题自测的清单很多线上问题都是照着这个顺序查出来的网络层telnet 测试端口是否通ping 排查延迟。连接层INFO clients 看连接数检查连接池配置。命令层SLOWLOG 查慢命令BIGKEYS 扫描大 key。持久化层INFO persistence 看 RDB/AOF 是否正常检查最近一次 save 是否有失败。资源层INFO memory 看内存使用和碎片率INFO cpu 看上下文切换。复制层INFO replication 看主从偏移量主从延迟过大时读请求可能读到旧数据。我实际用过这套流程解决了不少问题其中最典型的是一次内存暴涨排查当时表面原因是容器重启后 Redis 从 RDB 加载数据导致内存短暂翻倍实际上是因为 RDB 和 AOF 同时开启且 RDB 文件偏大。看完 INFO 才发现加载阶段内存开销远超预期最后通过调整混合持久化配置和限流初始化连接解决了。10. 几个我亲测有用的实践经验写到最后分享一些踩过坑之后沉淀下来的实操经验。不一定每条都适合所有项目但多数情况下能让你的 Redis 少出问题。第一生产环境尽量少用KEYS命令我见过同事在 Spring 项目里用KEYS user:*扫线上缓存直接把 Redis 卡住几秒。取而代之用SCAN cursor MATCH pattern COUNT count分批次遍历注意 SCAN 返回的游标可能不保证完整遍历所以只适合统计和清理场景不适合拿来做业务强依赖的快照。第二设置合理的maxmemory和淘汰策略。默认的策略是noeviction内存满了直接拒绝写入很多业务会莫名其妙报错。线上我一般用allkeys-lru或volatile-lru具体看缓存是否允许淘汰。这个参数按业务容忍度来不能照搬公司其他项目配置。第三每个 key 都要有命名规范。我习惯用业务名:实体名:ID[:子对象]这个格式比如order:1001:items。这不仅是可读性问题后面做权限控制、监控、删除缓存、问题定位都会省很多事。没有规范的 Redis 集群基本就是一个大型垃圾场。第四写脚本的时候一定加过期时间。之前遇到过没设过期时间的 key 慢慢堆积内存从 4GB 涨到 40GB。后来我写了一个扫描脚本把所有无 TTL 且不应该是常驻数据的 key 都列出来逐个确认清理。从此我要求所有缓存写入必须依赖 TTL。如果有些 key 需要长期保留那就显式说明理由不要默认留一个不超时的 key。第五Redis 版本升级要谨慎。官方 6.x 引入了 ACL、7.x 引入了多部分 AOF 和函数等新特性但老客户端不一定兼容。我见过一次升级到 7.x 后旧版本 Jedis 在部分命令上行为异常的问题所以生产升级前至少要在测试环境完整跑一遍压测和业务回归。这套东西从安装、数据类型、缓存治理、持久化、分布式锁到集群基本覆盖了我这几年在项目里真正用到的 Redis 知识点。工具和版本会变但底层的数据结构思维、并发问题意识和故障排查逻辑是通用的把这些抓住后面不管换什么缓存产品都能快速上手。