
这几年做后端Redis 几乎躲不开。从最开始只会 SET/GET到后来负责缓存治理、排分布式锁、搭过主从和 Cluster再到最近把 Redis 集群从裸机迁到 K8s 上踩过的坑凑一凑都能整理成一本小册子。这篇 Redis 学习笔记是我把“从安装到实战”这条路重新捋了一遍的记录覆盖了 Windows、macOS、Docker 三种安装方式五种核心数据类型怎么和真实业务对应缓存穿透、击穿、雪崩怎么处理以及持久化、日志、集群和常见超时报错的排查思路。适合刚想入门 Redis 的同学照着敲一遍也适合准备面试前查缺补漏的老手。下面直接进入正文。1. 环境准备先把 Redis 跑起来很多人的第一道坎其实不是 Redis 命令而是这玩意儿装不上、启动不了。尤其是 Windows 用户第一次搜“Redis 官网”会发现官方压根没有 Windows 安装包只有 Linux 源码。所以这一节先把安装讲透。1.1 Windows 下装 Redis别再傻傻下官网了Redis 官方不提供 Windows 版本但社区里一直有移植版。早期最出名的是微软维护的 3.x 版本后来慢慢没人管了接着大家常用的是 tporadowski 的 5.0.14.1这个版本我用下来最稳支持到 Redis 5.0 的全部特性也是网上“redis for windows 5.0.14.1”这个词的来源。如果你在网上搜到 4.0.8那是更老的古董除非项目锁死否则建议别碰。安装步骤很简单下载 redis-x64-5.0.14.1.zip解压到比如 D:\redis。打开 cmd 进入该目录运行redis-server.exe redis.windows.conf。另开一个终端运行redis-cli.exe ping返回 PONG 就说明服务起来了。如果不想每次手动启动可以注册为 Windows 服务redis-server.exe --service-install redis.windows.conf --service-name Redis redis-server.exe --service-start --service-name Redis这里给个配置上的提醒Windows 版解压后默认是 127.0.0.1:6379protected-mode默认开启本地开发完全够用。生产环境千万别用 Windows 版Log 和 Fork 行为都有差异线上出问题你很难在 Linux 上复现。如果你装了 Docker Desktop我更推荐直接用 Docker 跑 Linux 版后面的 1.3 会讲。1.2 macOS 安装 Redis一行命令的事macOS 上最推荐用 Homebrewbrew install redis brew services start redis redis-cli -h 127.0.0.1 -p 6379 pingHomebrew 默认装的是当前最新稳定版。如果项目需要指定版本可以brew install redis7然后用redis-server --version确认版本。这里有个小坑Mac 上如果之前用过旧版本升级后配置文件路径可能在/usr/local/etc/redis.confIntel或/opt/homebrew/etc/redis.confApple Silicon别找错地方。1.3 Docker 安装 Redis 和主从一条命令启动Docker 是最省心的方式尤其是你要同时装多个 Redis 实例做主从测试的时候。先拉镜像docker pull redis:7.2单机启动docker run -d --name redis-single -p 6379:6379 redis:7.2 redis-server --appendonly yes想验证 AOF 持久化是否生效可以直接在容器里看docker exec -it redis-single redis-cli info persistence搭建主从是学习流里绕不开的一步。我的做法是自定义一个 Docker 网络避免写死 IPdocker 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-slave --network redis-net -p 6380:6379 redis:7.2 redis-server --slaveof redis-master 6379注意这里--slaveof后面跟的是容器名而不是 IP这样容器重启后 IP 变了主从关系也不会断。验证主从是否建立docker exec -it redis-slave redis-cli info replication看到master_link_status:up就说明成功了。这种临时主从非常适合本地验证复制延迟、故障切换这些场景。1.4 设置密码别让 Redis 裸奔在公网上Redis 默认无密码如果你把 6379 端口暴露到公网扫描工具一分钟就能扫到然后被写入木马或挖矿程序这种案例在网上太多了。设置密码有两种方式一是修改配置文件 redis.confWindows 上是 redis.windows.confrequirepass YourStrongPassword二是运行时动态设置redis-cli CONFIG SET requirepass YourStrongPassword redis-cli CONFIG REWRITE客户端连接时redis-cli -a YourStrongPassword或者在交互模式下先执行AUTH YourStrongPassword。我个人的习惯是即使在内网也至少设置一个复杂密码同时把bind改成内网 IP不要用0.0.0.0。Windows 用户最常见的连接失败就是从另一台机器连不上本机 Redis十有八九是bind 127.0.0.1没改。2. 数据类型与命令速查学习 Redis 的第一课Redis 的看家本领就是它的数据类型。面试的时候张口就能说出五种类型不难难的是每种类型到底解决什么业务问题。这一节把类型和应用场景串起来讲。2.1 五种数据类型到底在业务里怎么用String最简单的键值对用来做缓存、计数器、分布式 ID 都没问题。比如INCR实现点赞数、库存扣减。List有序列表适合做消息队列、时间线。LPUSHBRPOP可以搭一个最简单的可靠队列。Hash适合存对象比如用户信息、商品详情。一个 key 对应多个 field省内存还能单独操作某个字段。Set无序去重集合适合做标签、关注关系、共同好友。SINTER算交集很方便。ZSet有序集合每个元素带 score适合做排行榜、延迟队列。我用它做过排行榜直接用ZREVRANGE取 Top N比在数据库里 ORDER BY LIMIT 快太多了。Redis 6.0 之后还有 Stream专门做消息队列支持消费者组HyperLogLog 用来做基数统计比如 UVGeo 做附近的人Bitmap 做签到统计。这些属于进阶内容等基础类型用熟了再扩展也不迟。2.2 高频命令速查表命令太多不可能全背但下面这张表建议刻在脑子里类型常用命令通用SET GET DEL EXISTS EXPIRE TTL TYPEStringMSET MGET INCR DECR SETNX SETEXListLPUSH RPUSH LPOP RPOP LRANGE LLENHashHSET HGET HGETALL HDEL HINCRBYSetSADD SREM SMEMBERS SISMEMBER SINTERZSetZADD ZRANGE ZREVRANGE ZSCORE ZINCRBY这里特别提醒一个生产环境的坑KEYS *命令千万不要在生产上用它会把所有 key 遍历一遍阻塞 Redis 主线程。真要扫描 key用SCAN每次返回少量数据不会把实例打挂。还有一个高频场景给 key 设置过期时间。EXPIRE key 60和SETEX key 60 value都可以但要注意如果 key 已经存在SETEX会覆盖旧值。判断 key 是否还能活多久用TTL返回 -1 表示永久-2 表示 key 不存在。2.3 过期删除与内存淘汰Redis 内存不够了怎么办很多人以为设置了过期时间时间一到 key 就会立刻消失其实不是。Redis 的过期策略是惰性删除 定期删除的组合惰性删除是在访问 key 时才检查是否过期定期删除是每 100ms 抽一批 key 检查。因此一个过期的 key 如果没有再被访问可能会在内存里躺一段时间等到内存紧张才被回收。真正决定 Redis 不被打爆的是内存淘汰策略。配置项是maxmemory-policy常用的几个noeviction写不进去直接报错。allkeys-lru按 LRU 淘汰全部 key缓存场景最常用。volatile-lru只淘汰设置了过期时间的 key。allkeys-lfu按 LFU 淘汰适合有热点访问的缓存。实际设置redis-cli CONFIG SET maxmemory 512mb redis-cli CONFIG SET maxmemory-policy allkeys-lru我曾经遇到过一个事故缓存服务把maxmemory-policy配成了noeviction结果业务量上来后新的写入全部失败页面接口超时。排查了很久才找到是淘汰策略的问题。所以缓存业务我一般建议用allkeys-lru虽然理论上可能把一些重要的 key 淘汰掉但总比整个服务不可用强。3. 缓存治理从穿透到雪崩Redis 最核心的应用是缓存但缓存不是“存进去就完了”。缓存穿透、缓存击穿、缓存雪崩这“三兄弟”是面试必问实战也必踩我这里一个个展开。3.1 缓存穿透、缓存击穿、缓存雪崩怎么区分和解决很多人记不住这三个词的区别我用一句话总结穿透请求一个“缓存和数据库都不存在”的数据每次都打到数据库。击穿某一个热点 key 过期瞬间大量请求同时打到数据库。雪崩大量 key 同时过期或者 Redis 整体宕机所有请求打到数据库。解决穿透最常用两个方案缓存空值或者布隆过滤器。缓存空值很好理解查询结果为空也往 Redis 里写一个 null设置短过期时间比如 60 秒。布隆过滤器更优雅把所有可能存在的 key 先过滤一遍不存在的直接挡在 Redis 外面。解决击穿的思路是“只让一个请求去查数据库”。常见做法是加互斥锁在缓存失效后先尝试SET key value NX PX 30000拿到锁的人才允许查数据库其他线程 sleep 一会儿后重新查缓存。另一个思路是逻辑过期写过期的 value但 value 里带一个逻辑过期时间后台线程发现过期后异步刷新缓存。解决雪崩的思路是让 key 的过期时间尽量分散。比如在设置过期时间时加一个随机数int expire 300 new Random().nextInt(60);这样即使大量 key 是同一批写入的它们不会在同一秒全部过期。另外可以用多级缓存兜底Redis 挂了还有本地缓存扛着。Redis 本身也要考虑高可用也就是第 5 节要说的主从和哨兵。3.2 缓存与数据库的一致性先更新谁是个经典问题缓存和数据库的一致性是分布式系统里最烦人的问题之一。最常用的模式是 Cache Aside读先读缓存命中直接返回未命中读数据库再回填缓存。写先更新数据库然后删除缓存或者更新缓存。很多教程推荐“先更新数据库再删除缓存”原因是如果先删缓存在缓存被删掉到数据库更新完成之间可能有别的请求把旧数据回填到缓存导致缓存里一直是脏数据。先更新数据库再删缓存虽然也有时间窗但概率小很多。更进一步的方案是“延迟双删”先删缓存再更新数据库过几百毫秒后再删一次缓存。这个延迟时间需要根据业务调整目的是等旧缓存的回填动作完成后再清一次。说实话这个方案也不好做到绝对一致最终一致性最好还是靠监听数据库 binlog把删除缓存的操作放到消息队列里异步执行。我的经验是不要追求“强一致”在大多数业务场景下接受几百毫秒内的短暂不一致即可。把缓存理解为数据库前的一道“加速墙”而不是“同步副本”。3.3 用 Redis 做分布式锁别直接 SETNX面试里分布式锁是高频题。很多人第一次写的版本是SETNX lock_key 1 EXPIRE lock_key 30这套写法有问题如果 SETNX 之后进程崩溃了EXPIRE 没执行锁就永远不释放变成死锁。而且即使手动加上了 EXPIRE如果业务执行时间超过了过期时间锁被自动释放另一个线程拿到锁前面的线程执行完后又 DEL 了一把别人的锁连锁反应就来了。正确的姿势是用一条命令同时设置 value 和过期时间SET lock_key request_id NX PX 30000释放锁的时候要比较 value 是不是自己的 request_id不能直接 DEL。这个判断和删除要保证原子性最稳妥的方式是用 Lua 脚本if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end在实际项目里我建议直接用 Redisson 封装的RLock它自带看门狗机制会自动续期避免业务没跑完锁就过期的问题。对于 RedLock 红锁方案业界争议不小我个人的态度是如果你的场景连 Redis 主节点都会宕机那应该考虑的是系统整体可用性而不是去追求一个分布式锁的绝对安全。3.4 序列化为什么 RedisTemplate 存进去的东西 read 不出来Java 开发用 Spring Data Redis 时经常遇到这样的场景用RedisTemplate存了一个对象用redis-cli看却是一堆\xAC\xED\x00\x05t...乱码取出来还报类型转换异常。这就是序列化器配置不对。Spring 默认的JdkSerializationRedisSerializer会把对象序列化成二进制可读性差而且 Java 类和 Redis 之间没法跨语言访问。建议改成 JSON 序列化。我常用如下配置Bean public RedisTemplateString, Object redisTemplate(RedisConnectionFactory factory) { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(factory); StringRedisSerializer keySerializer new StringRedisSerializer(); GenericJackson2JsonRedisSerializer valueSerializer new GenericJackson2JsonRedisSerializer(); template.setKeySerializer(keySerializer); template.setValueSerializer(valueSerializer); template.setHashKeySerializer(keySerializer); template.setHashValueSerializer(valueSerializer); template.afterPropertiesSet(); return template; }有几个注意点key 建议统一用 String 序列化方便排查value 用 JSON 序列化后对象里最好保留类型信息否则反序列化时会变成LinkedHashMap。另外用StringRedisTemplate操作字符串是不会乱码的但存复杂对象就用自定义的RedisTemplate。4. 持久化、日志与故障排查线上 Redis 如果只当缓存丢了数据还能靠 DB 顶住但如果当作中间件存了业务数据持久化就很重要。这一节把持久化和日常排查讲清楚。4.1 RDB 和 AOFRedis 重启后数据还在吗Redis 默认用 RDB 持久化会在指定时间间隔生成内存快照。典型配置是save 900 1 save 300 10 save 60 10000意思是 900 秒内至少 1 次写操作就生成快照300 秒内至少 10 次60 秒内至少 10000 次。RDB 的优点是恢复速度快、文件紧凑缺点是两次快照之间的数据会丢。AOF 则是以日志形式记录每次写命令配置appendfsync everysec时最多丢 1 秒的数据。AOF 文件会越来越大需要定期BGREWRITEAOF重写。Redis 4.0 之后支持混合持久化既用 RDB 做快照又用 AOF 增量记录兼顾速度和安全性配置项是aof-use-rdb-preamble yes。选择策略上如果是纯缓存不需要持久化如果 Redis 里存了不能丢的数据用 AOF everysec如果既想快恢复又不想丢太多开混合持久化。我之前在一台 32G 内存的机器上做过测试RDB 恢复一个 5G 的实例只要十几秒AOF 重放就得几分钟性能差异很明显。4.2 Redis 日志和慢查询别等出了问题才看Redis 默认日志输出到 stdout容器里可以直接看 docker logs物理机建议配置 logfilelogfile /var/log/redis/redis-server.log loglevel notice日志级别从低到高是 debug、verbose、notice、warning生产建议 notice。debug 日志量大本地调试才用。排查性能问题更常用的是慢查询日志。配置slowlog-log-slower-than 10000 slowlog-max-len 128单位是微秒10000 微秒就是 10ms。设置完后用SLOWLOG GET查看慢命令。之前我排查过一个诡异超时最终定位到一条KEYS *命令在百万 key 的实例上执行了足足 800ms直接把主线程卡住了。从那以后我对生产环境 redis-cli 有个铁律禁用 KEYS禁用 FLUSHALL。4.3 常见报错与排查实录这里把搜索结果里大家问得最多的几个问题整理成速查表报错现象可能原因排查方向Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutExceptionLettuce 客户端默认超时 60s连接池耗尽或 Redis 主线程阻塞查慢查询日志看 Redis 是否阻塞检查连接池 maxActive 配置客户端 timeout 调小并加熔断docker search redis request returned 500 internal server error ...Docker daemon 没启动或 Docker Desktop 引擎异常重启 Docker Desktop检查 daemon 状态别用老版本 Docker ToolboxWindows 上 redis 4.0.8 启动闪退运行库缺失、配置文件路径错误改用 5.0.14.1 或 Docker 方式Redis Desktop Manager / Another Redis Desktop Manager 连接不上bind 只绑了 127.0.0.1、protected-mode 未关、密码错误检查 bind、protected-mode、requirepass以及云安全组端口重点说一下 Lettuce 超时的问题。Spring Boot 默认的 Redis 客户端就是 Lettuce如果代码里没有显式配置超时时间默认是 60 秒。在高并发场景下连接池不够用请求排队很容易触发RedisCommandTimeoutException。解决思路是三层第一给线程池和连接池留足够余量第二把超时时间设得合理比如 3 秒第三业务侧做熔断降级不能让 Redis 超时拖垮整个应用。日志排查建议先看 Redis 自己的 log再看应用侧日志里的堆栈是在哪个命令超时最后用redis-cli --latency看客户端到 Redis 的网络延迟。我遇到过一种情况应用和 Redis 在同一个局域网但网络延迟偶尔飙到 200ms查了一圈发现是网卡多队列和交换机 over-subscription 的问题这种物理层面的问题只能靠监控去发现。5. 集群、容器化与进阶路线Redis 单机再快也有上限。当内存达到几十 G、并发到十几万的时候就要考虑集群了。最后这一节聊集群、K8s、可视化工具和面试题。5.1 从主从到哨兵再到 Cluster主从复制是 Redis 高可用的基础。主节点负责写从节点负责读可以用info replication查看复制状态。主从解决了“单点故障”里的“数据备份”问题但主节点挂了从节点不会自动顶上。哨兵Sentinel机制就是专门做自动故障转移的哨兵集群监控主从节点发现主节点挂了会从从节点中选举一个提升为主。哨兵的配置相对复杂但原理很简单心跳检测 投票选举。Cluster 模式则是 Redis 官方的分布式方案。数据通过哈希槽分片总共 16384 个槽每个节点负责一部分槽。客户端连接任意一个节点都能路由到正确的位置。Cluster 解决了单实例容量上限的问题适合数据量大、需要水平扩展的场景。三者的选择很简单数据量不大且能容忍服务中断一小会儿用主从 哨兵数据量大、需要横向扩展直接上 Cluster只做本地开发主从就够了。5.2 K8s 上部署 Redis 集群的思路把 Redis 集群迁到 K8s 之后和裸机最大的区别是 Pod 的 IP 会变所以不能用 IP 来配置节点间通信。Redis Cluster 的节点发现依赖每个节点的cluster-announce-ip和cluster-announce-port在 K8s 里通常配合 StatefulSet Headless Service 使用让每个 Pod 有固定的 DNS 名字。部署时可以这样设计用 StatefulSet 创建 6 个副本3 主 3 从。为每个节点配置独立的 configmap指定cluster-enabled yes和节点 announce 地址。使用 Headless Service 提供稳定网络标识比如redis-0.redis-headless.namespace.svc.cluster.local。用redis-cli --cluster create命令把所有节点加入集群。比较麻烦的点是缩容。Redis Cluster 在 K8s 里缩容需要先把对应节点上的槽迁移走否则会丢数据。这里强烈建议用 Redis Operator 自动化管理不要手写一堆 Deployment 去维护。5.3 可视化管理工具选哪个我电脑上装了好几个 Redis 客户端平时轮换着用。Redis Desktop Manager老牌工具社区版已经停更新版本收费功能全但启动偏慢。Another Redis Desktop Manager完全开源免费GitHub 上很活跃Windows/macOS/Linux 都有支持连接 Cluster、查看 key、执行命令我最常用的是它。Redis Insight官方出品免费UI 现代带实时监控、慢查询分析适合做性能调优。如果你是运维要用慢查询分析和内存分析Redis Insight 最好用如果你是纯开发只连一下看 keyAnother Redis Desktop Manager 轻量顺手。连接工具本质上都绕不开 Redis 协议关键是支持不支持 Cluster 拓扑以及能不能对二进制 key 做解码。5.4 面试高频题与学习路线照着查缺补漏结合这么多年被问到的和面别人的经历Redis 面试题有这几个方向为什么 Redis 快单线程为什么快本质是基于内存 多路 IO 复用 高效数据结构单线程避免锁竞争。Redis 6.x 引入了多线程 IO但执行命令还是单线程要分清 IO 线程和命令执行线程。RDB 和 AOF 怎么选怎么保证重启后尽量不丢数据缓存穿透、击穿、雪崩各自的解决方案是什么分布式锁怎么实现SETNX 的问题在哪Redission 看门狗是什么原理ZSet 底层是跳跃表 哈希表为什么用跳跃表不用红黑树主从复制的链路是什么复制积压缓冲区有什么用集群数据分片是怎么做的为什么是 16384 个槽学习路线方面我的建议是先啃官方文档redis.io的命令列表不用全背用熟前十种然后自己搭一个主从再模拟主节点宕机看哨兵怎么切换接着用 Redis 做事务、发布订阅、Stream最后深入源码先看 SDS 和跳跃表再看事件驱动模型。能把这套下来入门到进阶就通了。最后想再说点我的个人体会。学 Redis 最容易犯的错就是“只看命令不搭环境”。命令背得再熟不如自己在电脑上把主从搭一遍把 AOF 打开把内存淘汰策略改一改亲眼看看 Redis 是怎么反馈的。还有Redis 的配置项很多但别一上来就给生产环境堆花活很多默认值就是社区长期踩坑后的最优解改每一个配置前都先想想这个官方的默认值是不是已经够用了带着这个问题去学习和踩坑你会比同龄人少走很多弯路。