
聊到 Redis 核心应用场景后端的第一反应通常是“缓存”但缓存只是它能力的入场券。我在前后端都折腾过的这几年Redis 在项目里承担过分布式锁、排行榜、附近的人、幂等记录、队列削峰甚至临时数据结构中转站几乎没有哪个项目能完全绕开它。这篇文章没有教科书式的罗列而是把 Redis 从安装落地到实际场景的整个路径走一遍你会看到每个方案背后的取舍也会看到我踩过的那些不太体面的坑。适合刚上手 Redis 的开发者也适合准备把它用到生产环境的团队。很多人会背 Redis 面试八股文但一落到具体需求就不知道用哪种数据类型也有人把缓存穿透、缓存雪崩当概念挂在嘴边真出问题时又分不清是击穿还是穿透。所以我打算从“跑起来”开始一路讲到缓存治理、分布式锁、持久化、集群和实战排查最后附上高频命令速查。每个场景我都会给结论、给步骤、给坑这样你在自己的项目里可以直接照着做。1. 先让 Redis 跑起来安装、连接和第一次启动1.1 不同环境下的安装姿势很多初学者卡在第一步。Redis 官方其实明确表示不支持 Windows但开发机是 Windows 又是大多数人的常态。这里我给出三种可行的路线按我的推荐程度排序。Docker 方式最省心。安装 Docker Desktop 后执行docker run -d --name redis -p 6379:6379 redis:7.2然后docker exec -it redis redis-cli ping就能看到 PONG。想要带配置启动就把 redis.conf 挂载进去docker run -d -p 6379:6379 -v /my/redis.conf:/etc/redis/redis.conf --name redis redis:7.2 redis-server /etc/redis/redis.conf。WSL 方式次之。在 Windows 上装好 WSL 后进 Ubuntu 里执行apt install redis-server或者去 Redis 官网下载源码编译体验和 Linux 一致。社区版 exe 不建议用于生产。像 redis for windows 5.0.14.1 这类封装版在本地练练手可以但版本落后还可能踩到文件句柄和内存映射的坑。Windows 上的 redis windows 下载包很多真要选认准官方仓库里由社区维护的版本。macOS 就简单多了。有 Homebrew 的话brew install redis装完直接brew services start redis开机自启也帮你搞定了不想常驻的话就redis-server /opt/homebrew/etc/redis.conf手动起。Linux 服务器上的安装就更套路Ubuntu/Debian 用apt install redis-serverCentOS/RHEL 用yum install redis装好后 systemctl 管理。无论哪种环境装完第一件事一定是redis-cli ping能回 PONG 说明服务起来了后面才能继续聊场景。这里必须提醒一句单机版部署虽然最快但生产环境至少要从主从复制开始考虑。docker 安装 redis 主从也很简单后面讲高可用时会专门说明第一步先别急着玩集群。1.2 可视化客户端选型从 redis-cli 到桌面管理器命令行用久了会形成肌肉记忆但排查线上问题时尤其要看一堆 key 的分布和过期时间桌面工具效率高得多。我先后用过三代客户端可以给你做个参考。Redis Insight 是官方出的可视化客户端免费界面现代支持命令面板、慢日志、内存分析还能看 pub/sub我最推荐这个。Another Redis Desktop Manager 是当初 RDM 收费后大家迁移最多的工具支持树形展示、多连接管理扫描大数据量 key 时比较稳社区活跃度也不错。老牌的 Redis Desktop Manager 目前在 macOS 和 Windows 上很多版本开始收费能找到的免费版往往旧不建议再用。连接工具最常遇到的问题不是配置错误而是 redis.conf 里bind 127.0.0.1和protected-mode yes的组合把人挡在门外。本地连没问题远程一连就 timeout。早期我不知道protected-mode这个机制非要在服务器上开 6379 端口再用桌面端连结果连不上后来一看日志才发现是保护模式把外部连接拒绝了。正确做法是绑内网 IP 或用防火墙控制访问不要直接裸奔公网开发环境图省事可以protected-mode no但生产环境我强烈建议配合 ACL 和密码一起用。还有一个常见现象是连接工具能连上但一执行KEYS *就卡死。这通常是线上 key 数量太大KEYS命令会阻塞 Redis 单线程。用工具打开时要小心尽量选用扫描方式后面在“常用命令速查”里再展开说。1.3 首次启动必做的三件小事密码、持久化、日志装好后先别急着写业务代码把配置基座打稳。密码这一项早期的 Redis 配置简化成了requirepass yourpassword客户端连接后执行AUTH yourpassword。Windows 设置 redis 密码也是改 redis.windows.conf 里的同一个字段只是重启时要指定配置文件。现在 Redis 6 更推荐用 ACLACL SETUSER default ON yourpassword ~* all。用 ACL 的好处是可以给不同业务线分配不同权限比如让某个应用只能访问自己业务前缀的 key。持久化配置决定了你 Redis 里数据能不能在重启后幸存。第一次使用可以先开 AOFappendonly yes再设置appendfsync everysec。同时保留默认的 RDB 快照规则作为兜底。这块后面会单开一章细讲但起步阶段这两行配置一定不能漏否则你缓存里存的数据一重启就全没了到时候排查起来像灵异事件。日志这一项容易被忽略。默认 Redis 是输出到 stdout 的用 systemd 或 Docker 能看见但手动redis-server时最好指定logfile /var/log/redis/redis.log和loglevel notice。真出问题时redis.log里的信息比客户端报错有用得多尤其是主从同步和持久化失败都会先在这里留下痕迹。2. 数据类型是地基五大基础类型 高级类型的场景映射2.1 五大数据类型在真实需求里的位置聊到 Redis 核心应用场景数据类型必须放在前面。很多人背过 String、Hash、List、Set、ZSet 这五种类型但背完还是不会选。我的经验是反向思考先看业务要做什么操作再挑最省内存和最自然的数据结构。String 适合单值缓存、计数器、分布式 Session。比如用户登录态可以先序列化 JSON 存进 String网页浏览数直接INCR article:read:10086。要注意 String 本质是二进制安全的字节数组能存数字、文本、序列化对象但别什么对象都往里塞超过几百 KB 的大 value 就是潜在的 Big Key 隐患。Hash 是对象模型的好帮手。用户信息存成HSET user:profile:1001 name zhang age 18想改年龄时HINCRBY user:profile:1001 age 1不用像 String 那样读出整个 JSON 再改回去。缓存数据库一条记录时很多人习惯直接SET user:1001 {...}但如果热点用户会被频繁改单一字段Hash 更省流量也更灵活。List 最常见的用途是简易队列和最新的列表。用LPUSH塞数据BRPOP阻塞读就能实现一个生产消费模型。还有“最新评论”这种场景LPUSH post:comments:2000 first commentLTRIM post:comments:2000 0 99只保留最近一百条天然做成了列表页。但 List 做消息队列有个缺点消费端如果宕机消息就丢了可靠场景后面会讲 Stream。Set 适合去重、标签、抽奖。关注关系SADD user:1001:follow 2002 2003判断是否关注用SISMEMBER。抽奖可以用SPOP随机弹出一个或者SRANDMEMBER只读取不弹出。日活用户也能用 Set 存储但数据量大时我更倾向用 Bitmap 或 HyperLogLog内存差距很大。ZSet 是应用最广的“排行榜神器”。每个成员带着一个 score天然支持排序。ZADD leaderboard:game1 1000 userAZREVRANGE leaderboard:game1 0 9就能拿到前十。延迟队列也可以伪装成 ZSetscore 存执行时间戳轮询时ZRANGEBYSCORE queue:task -inf NOW取出到期任务再到ZREM删除。还有限流算法中的滑动窗口用 ZSet 实现也很自然后面统一说。2.2 高级类型Bitmap、HyperLogLog、GEO 把冷门场景变成亮点除了五种基础类型Redis 里还有几个容易在面试里加分、实战里省内存的类型。Bitmap 本质是 String 的位操作适合做布尔状态统计。比如用户签到SETBIT user:sign:202501 1 1表示 uid 为 1 的用户今天签到BITCOUNT user:sign:202501就能统计全量签到人数。一万个用户做一万天签到占用的内存远比 Set 小得多。HyperLogLog 适合做 UV 统计。PFADD page:view:home uuid-1 uuid-2PFCOUNT page:view:home直接算出近似去重数量。注意它是有误差的官方误差在 0.81% 左右对于 UV 这种量级完全可以接受。你千万别用 Set 去存每个访问用户的 ID量一大内存就崩了。GEO 用于位置相关场景。GEOADD nearby:drivers 116.39 39.90 driver-1GEORADIUS nearby:drivers 116.40 39.91 5 km ASC COUNT 20就能拉出附近五公里的司机。它底层其实是个 ZSet所以只能用 GEO 前缀的命令去操作别混着用。附近的人、附近门店、货车围栏这些需求都是它的第一现场。2.3 键设计与过期策略别让 key 成为事故用 Redis 时最容易被忽略的是 key 的命名规范。我见过一个项目里有人用user1、u-1001、1001_profile三种风格导致统计和管理极其混乱。建议统一格式业务域:对象:ID[:子对象]。例如user:profile:1001、order:list:2001:items。这样在可视化客户端里浏览、用 SCAN 匹配前缀、设置按业务的过期扫描都很清晰。过期策略一定不能偷懒。内存是有限资源缓存数据必须有一个明确的 TTL。我用SETEX或EXPIRE时习惯在业务里统一封装一个时间枚举比如缓存 5 分钟、1 小时、一天而不是随手写一个 86400这样后面调整时好改。另外前面提到的 RDB/AOF 持久化和 TTL 并不冲突过期 key 即使被持久化恢复后依然会按规则失效。再就是 Big Key 管理。一个 Hash 里有上百万个 field或者一个 String 有几 MBRedis 处理这类 key 时会让单线程命令变慢网络传输也卡顿。我的经验是先用redis-cli --bigkeys扫一遍把最大的 key 揪出来String 大 value 可以压缩后再存Hash 可以按业务拆成多个小 key或者考虑直接丢到对象存储里。删除时也要注意大 key 用DEL会阻塞Redis 4.0 之后可以用UNLINK异步删除。3. 缓存治理穿透、击穿、雪崩不能只在面试里答得漂亮3.1 缓存穿透空值缓存 布隆过滤器哪个更实用缓存穿透的本质是请求一个缓存和数据库里都不存在的数据。比如用户模块攻击者拿一排不存在的 uid 直接打过来每次都会穿透 Redis 打到数据库。这个场景下缓存形同虚设数据库压力瞬间被放大。第一种方案是缓存空值。查数据库发现没有就在 Redis 里写一个null或特殊标记比如user:profile:999999TTL 给个 30~60 秒。但这要求你能区分“空值缓存”和“真实数据”建议用统一前缀或 JSON 里的一个字段标记。缺点是空数据太多会占内存而且可能在一小段时间内掩盖了真实数据的写入但影响不大。第二种方案是布隆过滤器。先在启动时把存在的 user id 全部 load 到布隆过滤器里请求来了先查布隆过滤器如果不存在直接返回 404。布隆过滤器的原理是 bitmap 多个哈希函数存在一定的误判率但不存在的 key 一定不会误判。用 Redis 的 BIT 操作自己实现或用 Guava 的 BloomFilter 都行业务规模大时常见做法是在 Redis 里用 Redisson 的 RBloomFilter。我的经验是小规模系统用空值缓存就够了布隆过滤器反而增加维护成本但接口要防刷、对性能要求高布隆过滤器更值得投入。还有一点穿透不止发生在读不存在的数据也可能是写偏移量。比如热 key 被恶意传参成负数这种要在业务层做参数校验Redis 只是最后一道防线。3.2 缓存击穿互斥锁 vs 逻辑过期实战选哪个击穿是指一个热点 key 在过期的一瞬间大量并发请求同时发现缓存失效然后一起涌向数据库。缓存没有完全崩但被这一个 key 打垮。常见解法是互斥锁。在缓存失效后让第一个请求拿到分布式锁去查数据库回填缓存其他请求拿不到锁可以返回旧缓存、sleep 重试或直接等着。用 Redis 实现就是SET cache:rebuild:hotkey owner NX EX 3拿到锁的线程执行 DB 查询再SETEX写回缓存最后释放锁。这个方案逻辑清晰但会造成少量请求等待对一致性要求高的场景很合适。另一派做法是逻辑过期。存进缓存的值不只存业务数据还带一个expireAt字段。获取缓存时发现逻辑时间已过期先尝试拿到重建锁如果拿锁成功异步去查 DB 重建数据同时当前线程继续返回旧值。这样可以做到没有线程因为缓存过期而阻塞但代价是短时间里读到的可能是旧数据。热点活动页这种弱一致场景用逻辑过期很舒服电商库存这种强一致场景就别图省事。我在实战中遇到过把互斥锁用在所有 key 上的反面教材一把全局锁锁住了所有缓存重建导致吞吐量断崖下降。正确的做法是只对热点 key 加锁锁粒度精确到这个 key 本身而不是所有 key 共用一把。判断热点可以用访问量统计、热点预测表或者 Redis 自身的LFU策略辅助。3.3 缓存雪崩过期时间打散 熔断降级雪崩是大量 key 在同一时间过期或者 Redis 节点挂掉导致请求全部打到数据库。和击穿的区别在于击穿是“一个人引发洪峰”雪崩是“一群人集体摆烂”。最简单有效的预防手段是过期时间加随机数。假设你的业务缓存统一设置 1 小时写代码时别写死EXPIRE 3600改成3600 random(0, 600)让 key 的过期时间在半小时窗口内散开。这样能避免某一时刻集中失效。我之前接手过一个报表系统每天零点统一刷缓存加载完就全设 12 小时失效结果中午 12 点整数据库瞬间被打满。后来把 TTL 改成随机段问题就消失了。第二层防护是本地缓存兜底。查询链路变成“本地缓存 - Redis - DB”即便 Redis 出问题本地缓存能扛一波流量。一致性上要接受短时偏差可以通过定时刷新或版本号更新。现在 Caffeine 是 JVM 里比较好的选择配置起来也不复杂。第三层是服务端的降级和限流。在网关层或业务入口做一个开关Redis 故障时直接返回默认值或错误而不是让请求继续压库。这里我在生产环境踩过的坑是降级开关本身会变成一个新的热点比如每次请求都去远程配置中心拉开关配置中心反而被打挂。所以开关一定要有本地缓存并且支持人工强制关闭。3.4 Redis 序列化与缓存一致性乱码和双写不一致怎么解用 Spring Data Redis 的同学大概率见过这种诡异的 key\xAC\xED\x00\x05t\x00...。这就是默认 JdkSerializationRedisSerializer 干的好事Java 序列化后的字节流直接作为 key当然不可读。解决办法是自定义 RedisTemplatekey 用StringRedisSerializervalue 可以视情况选Jackson2JsonRedisSerializer或GenericJackson2JsonRedisSerializer。用 JSON 序列化还有个好处缓存里能直接看到内容排查问题不用反序列化工具。注意 LocalDateTime 这类类型需要额外配置 JavaTimeModule否则反序列化时报错是家常便饭。缓存一致性是另一个大坑。最常见的做法是 Cache Aside Pattern读的时候先查缓存没命中再查库最后回填写的时候先更新数据库再删缓存。这个模式理论上没问题但“先更新 DB后删缓存”如果删缓存失败下一次读就会拿到旧值并重新回填旧缓存。我的处理习惯是数据库更新成功后用可靠通道做删缓存的重试。比如把删除任务投递到本地消息表或 MQ由消费者去删。还有延迟双删先删缓存更新 DB隔几百毫秒再删一次解决并发读把旧值回填的问题。这里没有银弹必须结合业务容忍度选择。订单创建这类场景即使缓存短时间不一致也能接受因为缓存本来就不是绝对一致性的来源。4. 分布式锁Redis 锁的价值、细节与常见翻车现场4.1 从一个 setnx 到正确姿势分布式锁是 Redis 核心应用场景里含金量比较高的一个。Java 的synchronized只能锁单机多实例部署后必须有个跨进程的协调者。Redis 的SETNX天然适合做这件事。但注意千万不要再用两步法SETNX lock 1然后再EXPIRE lock 30。如果第一步成功了第二步崩溃锁就永远不释放。正确姿势是单命令原子设置SET lock:order:1001 uuid EX 30 NX。这个命令同时实现了“不存在才设置”和“自动过期”。解锁也需要原子操作。不能先GET判断 value 是不是自己的再DEL因为两步之间可能发生锁过期被别的线程抢走的竞态。要用 Lua 脚本保证“检查 value 删除”是一个整体if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 endvalue 必须用全局唯一 ID比如 UUID 或业务 traceId这能防止误删别人持有的锁。4.2 锁的续期、可重入和主从切换基础实现很简单但生产环境很快会遇到问题业务执行时间超过锁的过期时间怎么办锁自动释放后另一个线程进来了前面的业务还没干完等于两个人同时操作临界资源。Redisson 解决得比较好。它默认给锁 30 秒然后用一个定时任务周期性续期每过锁时间的三分之一就自动续一次这个机制俗称“看门狗”。如果拿锁的客户端崩溃了看门狗也停了锁到了过期时间就会释放不会死锁。Redisson 的RLock还支持可重入。同一个线程可以多次加锁底层用 Hash 结构记录重入次数。有些团队自己基于SETNX实现可重入靠 ThreadLocal 记录持有数也没问题但没必要重复造轮子。还有一个容易被低估的问题主从切换。假设锁写到了 mastermaster 宕机数据还没同步到 replica客户端 B 从新的主节点上又拿到了同一把锁。严格场景下可以用 RedLock 算法向多个独立 Redis 节点申请锁超过半数才算成功。但 RedLock 本身存在争议工程上很多团队都用单主 哨兵模式扛住了绝大多数场景。如果你是金融交易这种极端环境别自己拍板宁可多评估一下对一致性的要求。4.3 锁的粒度与排查笔记锁的粒度直接影响性能。你要锁的是“用户 1001 对商品 2001 的充值”那就用lock:recharge:1001:2001不要图方便用lock:recharge锁住全体用户。我见过因为锁粒度太大把一个支付接口的并发直接压成串行业务方还以为是数据库慢。锁内操作要尽量快。锁的本质是串行化临界区你在锁里查第三方接口、跑复杂计算、访问远程 RPC都会让其他线程等更久。更可怕的是锁内调用自身依赖的另一个 Redis 锁可能形成环状等待直接死锁。所以锁内只放必要的写操作其他事情在拿锁前做好。我在排查一次偶发超时时发现有一个定时任务拿锁后执行了十几秒的批量汇总而锁过期时间只设了 3 秒。相当于锁过期后另一台机器也进来跑同样的任务数据被算了两遍。日志里看不出崩溃因为根本没报错只是结果对不上。这类问题靠增加过期时间解决是治标不治本应该通过续期 拆批把锁持有时长降下来。5. 进阶场景消息队列、排行榜、限流和中间件边界5.1 Redis 做消息队列List、Pub/Sub 与 Stream 的取舍很多团队在消息量不大时直接用 Redis 顶替 MQ。确实能解决一部分问题但得选对模型。List 做队列最简单的是LPUSHBRPOP。生产者往里塞消费者阻塞读取。这个模型能实现异步削峰比如秒杀场景把创建订单请求先塞进队列消费者批量落库。缺点是没有消费确认任务处理到一半宕机消息就丢了恢复后也没法处理。如果想加强可以用BRPOPLPUSH把已取出的消息备份到另一个列表处理成功后再删。Pub/Sub 适合实时广播。比如通知所有节点的缓存失效一个节点更新了数据发一个PUBLISH channel:cache:clear其他节点订阅后清理本地缓存。但它是“发后即焚”订阅者没在线消息就没了所以别拿它做可靠通知。Stream 是 Redis 5.0 正式推出的消息队列模型。它支持持久化、消费者组、消息确认还有一个可视化工具 Redis Insight 能直观看到消息积压。如果团队不想引入 Kafka业务消息量又在百万级以内Stream 可以作为正经的轻量 MQ 使用。但真要追求事务、死信、大规模分布式分区专业 MQ 还是更靠谱。我的原则是一时翻译场景的项目可以用 Redis但核心链路要用 RabbitMQ 或 Kafka。5.2 排行榜、限流、计数器等高频玩法排行榜场景直接 ZSet。拿直播平台送礼物热度来说每次送礼ZINCRBY live:rank:room3 100 userA页面端ZREVRANGE live:rank:room3 0 9 WITHSCORES就能展示前十条。排行同分时要注意 ZSet 按 member 字典序排列不会完全符合业务预期可以额外维护时间戳因子。限流场景有两种常见做法。固定窗口INCR limit:user:1001后判断是否超过阈值再EXPIRE limit:user:1001 1秒级限流简单粗暴。滑动窗口用 ZSet 记录每次请求的时间戳ZREMRANGEBYSCORE key -inf (now-1s)删除窗口外记录再ZCARD统计当前窗口请求数。固定窗口有个临界问题窗口切换的瞬间可能突增双倍流量滑动窗口更平滑但内存开销也更大。计数器就是 String 的天下。日点击量、验证码发送次数、接口调用次数INCRBY一行搞定。需要注意的是计数器 key 的过期时间最好在第一次创建时就设好否则容易变成永不消失的无界 key。配合前面讲过的 TTL 规范这一块能干净很多。5.3 Redis 做中间件的定位哪些场景别硬扛很多公司把 Redis 当作“万能中间件”既是缓存又是数据库还是消息队列。这个用法在业务量小时没毛病但到了瓶颈期会非常痛苦。Redis 本质上是内存存储内存容量和成本都是瓶颈。如果你把冷数据、历史订单、用户全量资料都往 Redis 里塞别怪它动不动就 OOM。真正的数据库还是 MySQL、PostgreSQL 或分布式存储Redis 只放热数据。消息队列我上面说过对可靠性和分区扩展有要求就别硬扛。分布式锁不是全局事务的替代品多个资源要同时一致更新时用分布式事务或者本地事务消息不要迷信锁。还有一个容易踩的边界是“资源隔离”多个业务共用一个 Redis 集群一个业务的大 Key 或慢命令会把集群搞垮最好按业务线拆分实例或使用不同 db其实多 db 不推荐Redis 非 cluster 模式下 db0~db15 是逻辑隔离但性能上还是会互相影响。生产我习惯一个业务一套独立 Redis 或至少独立集群。在我负责过的微服务架构里Redis 的位置永远是“高速缓冲层 协同工具”它负责让常用数据离 CPU 更近、让跨服务状态能共享、让热点流量先被削掉一层。认清这个边界比掌握再多命令都重要。6. 持久化与高可用RDB、AOF、哨兵、集群一个都不能少6.1 RDB 和 AOF 怎么选丢数据 vs 恢复速度的博弈Redis 持久化有两种套路。RDB 是给整个数据集打快照恢复速度快但它只能恢复到上一次快照的时间点。比如每 5 分钟存一次宕机后最多丢 5 分钟数据。AOF 是追加日志每秒钟记录写命令重启后回放日志最多丢一秒钟数据但日志文件越来越大恢复也要慢一些。Redis 4.0 开始支持混合持久化RDB 做全量、AOF 做增量重启时先加载 RDB 再补增量速度和数据安全性得到了平衡。配置上RDB 默认是开启的控制 save 的规则AOF 默认关闭需要手动appendonly yes。我推荐生产环境直接开启 AOF并设置appendfsync everysec同时保留 RDB让它在每天凌晨做一次备份。这样即使 AOF 文件损坏也可以用 RDB 顶一下。这里有个容易被忽略的点AOF 文件是会膨胀的Redis 会自动触发BGREWRITEAOF重写。重写过程由子进程完成不阻塞主线程但如果磁盘 IO 很慢可能造成瞬时卡顿。我在低配云服务器上遇到过重写期间 CPU 飙高、命令延迟变大的情况换成了更大的磁盘后缓解了不少。6.2 主从复制与哨兵从单机版到高可用单机 Redis 挂了所有缓存和锁都会失效生产环境至少要搞主从复制。配置方式是在从节点的 redis.conf 里加一行replicaof master-ip 6379或直接启动时指定。Docker 里可以这样跑docker network create redis-net docker run -d --name redis-master --network redis-net -p 6379:6379 redis:7.2 redis-server --appendonly yes docker run -d --name redis-replica --network redis-net -p 6380:6379 redis:7.2 redis-server --replicaof redis-master 6379 --appendonly yes主从复制有一个特点从节点默认只读读写都打到 master读压力可以分流到 slave。同步过程先做全量同步再做增量同步中途断线了重连会尝试补发增量。光有主从还不够master 挂了还是需要人工切换。哨兵Sentinel就是来看门的监控 master 状态异常时自动把某个 slave 提升成 master。最少要部署三个 Sentinel 节点才能避免脑裂哨兵之间也要选举。在 K8s 环境里搭哨兵集群会比较繁琐推荐直接用 Redis Operator 或 Bitnami 的 Helm chart它们把 StatefulSet、Service、哨兵配置都封装好了。凡是自己手动在 K8s 上搭 Redis 的都逃不开存储编排和网络 DNS 的坑不是不能做而是耗时成本不低。6.3 Redis 集群槽位、扩容、客户端路由当数据量超过单机内存或者写并发大到单主扛不住时就需要 Redis Cluster。Cluster 把 key 空间分成 16384 个槽通过CRC16(key) % 16384算出槽位再分配到不同的主节点。客户端访问一个 key 时如果节点发现槽位不属于自己会返回 MOVED 错误聪明一点的客户端会顺着这个信息更新路由表。最少 3 主 3 从。创建集群的命令大概是redis-cli --cluster create 192.168.1.10:6379 192.168.1.11:6379 ... --cluster-replicas 1。但生产环境我不会手动去搭集群负载均衡和节点巡检太麻烦除非是几台机器的私有云。现在 Redis 7 集群运维比早期顺畅很多槽迁移用redis-cli --cluster reshard就能做但扩容期间对客户端和监控的要求比较高。Cluster 模式下要求每个节点尽量无大 key因为大 key 无法跨槽迁移多 key 操作也要保证这些 key 在同一个槽通常用 Hash Tag 算法把多个 key 塞进同一个槽。比如{order:1001}:items和{order:1001}:pay花括号部分参与哈希计算这样它们会被路由到同一个节点。这个技巧在实现事务和 Lua 脚本时尤其重要。6.4 常见连接报错与排查实录我在走上线环境时最常遇到的一个报错是Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException这个以 lettuce 为底层客户端的 Spring Boot 应用比较常见。排查顺序我一般是先看 Redis 端是不是满了redis-cli INFO看 connected_clients、blocked_clients、used_memory 是否异常高。再看业务代码是不是有慢命令SLOWLOG GET 10能看到哪些命令执行慢。大 key、KEYS *、频繁连接断开都是元凶。最后看网络客户端和 Redis 跨云区域时RTT 很高超时阈值要调大连接池配置太小也会因为等待连接而超时。另一个让我印象深刻的错误是关于 Docker Desktop 的docker search redis request returned 500 internal server error for api route and version http://%2f%2f.%2fpipe%2fdockerdesktoplinuxengine/v1.56/images/search?termredis, check if the server supports the requested api version这通常不是 Redis 的问题而是 Docker Desktop 的 Linux 引擎或 Docker API 版本异常。重启 Docker Desktop或者升级 Docker 版本一般就能解决。如果还不行检查 WSL 2 内核版本老内核和 Docker Desktop 新版有摩擦。还有连接工具一直转圈、AUTH失败也别急着怀疑网络。去看redis.log里有没有AUTH password called without any password configured这是典型 redis.conf 没生效的表现。Windows 设置 redis 密码后不重启或没指定正确的配置文件就可能出现这个状态。7. 把八股变成肌肉记忆高频场景速查与常见面试题7.1 为什么 Redis 快单线程和多线程的真相这个问题几乎是 Redis 面试题里的“第一题”。核心答案可以拆成四层数据全在内存IO 多路复用高效的数据结构设计命令执行单线程消除锁竞争。很多人听到“单线程”会惊讶Redis 6.0 不是有多线程 IO 吗准确说Redis 的主命令处理是单线程的多线程主要用在网络 IO 读写和异步持久化、异步删除上。执行命令的流程依然是单线程串行所以慢命令会阻塞其他所有命令。这就是为什么KEYS会被嫌弃O(N)复杂度在大 key 面前是灾难。了解了这一层很多面试题的答案就通了为什么 big key 会导致阻塞因为单线程处理一个超大 Hash 或重构大 key 时耗时长。为什么可以用UNLINK代替DEL因为它把释放内存的操作丢给后台线程处理。为什么 Redis Cluster 客户端会有 MOVED因为是分布式节点不共享状态。7.2 核心命令速查表命令不在多用对才算数。我整理了一份日常使用频率最高的命令集合可以当成速查卡。场景命令示例说明连接测试PING返回 PONG基础操作SET user:1:name zhang EX 60带过期时间检查过期TTL user:1:name-1 表示永久-2 表示不存在批量删除SCAN 0 MATCH user:* COUNT 100配合UNLINK异步删除对象存储HSET user:1:profile name zhang age 18Hash 单字段更新队列LPUSH task:queue job1/BRPOP task:queue 0阻塞读分布式锁SET lock:order:1 uuid NX EX 30原子设置排行榜ZADD rank:hot 100 article:1score 越高越靠前UV 统计PFADD uv:page 1/PFCOUNT uv:page近似去重位置查询GEORADIUS driver:location 116.39 39.90 5 km附近的人7.3 从场景反推设计而不是背命令面试官最烦的是背答案。与其把“五大数据类型”一字不差背出来不如让他看到你有场景能力。比如他问“如何实现一个排行榜”你应该说 ZSet。score 轮次积分member 用户 ID。如果分数相同要按时间顺序就在 score 上做编码或者额外用一个小 ZSet 存储入场时间。面试官再问“如何实现限流”你可以分固定窗口和滑动窗口并指出各自的临界问题和内存消耗。这才是 Redis 核心应用场景该有的解题思路。再比如“缓存穿透和布隆过滤器”别只提概念要能讲清楚误判率对业务的影响以及空值缓存最省事的适用条件。我面试人时能主动说出“热点 key 可以提前预热”的人会明显比背八股的更受欢迎。7.4 我的场景速查清单最后送你一份直接从实际业务提炼的清单照着这个去给项目体检基本不会漏用户信息缓存String 或 Hash业务前缀 IDTTL 15 分钟。分布式会话String 存 Session JSONTTL 30 分钟续期逻辑要兜底。接口级限流固定窗口用 INCR EXPIRE精细控速用 ZSet 滑动窗口。排行榜ZSet注意同分排序和定期清理过期成员。任务队列Stream搭配消费者组做 ACK。附近的人GEO省去自己算经纬度距离的麻烦。幂等记录StringSET idempotent:pay:1001 OK NX EX 3600。缓存穿透空值缓存 极短 TTL恶意攻击场景加布隆过滤器。缓存击穿热点 key 用逻辑过期普通 key 用互斥锁。缓存雪崩TTL 加随机偏移本地缓存兜底Redis 高可用。写了这么多说一点我自己的心得Redis 入门不难但把它用稳拼的是对数据结构的理解和故障现场的积累。我踩过最大的坑就是把缓存当数据库用脏数据一路流到线上报表才意识到 Redis 一致性治理不是写几个注解就能解决的。如果你刚开始用 Redis我建议先给自己定三条开发规范所有 key 必须带业务前缀并设置过期时间不使用 KEYS 命令任何缓存 value 都要考虑序列化后的可读性。这三条看起来简单但真能让你少熬好几个通宵。把缓存、锁、队列、统计这几个核心场景吃透不管面试还是项目落地都不会慌。