ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Redis实战指南:从安装到集群,一文吃透缓存核心原理

Redis实战指南:从安装到集群,一文吃透缓存核心原理 我接触 Redis 的第一天其实心里没什么敬畏感这不就是一个能存数据的 Key-Value 中间件吗后来在生产环境里连续熬了几个夜排查慢接口才明白越是看起来简单的东西越容易在细节上翻车。这次想好好写一篇给零基础同学看的东西把 Redis 从安装、核心数据类型、缓存治理、持久化到集群和常见排错完整串一遍。不管你是刚学后端还是已经在用 Spring Boot 做项目但不太清楚 Redis 内部机制这篇文章都能提供一条不绕弯的上手路径。真正用熟 Redis 之后你会发现它不只是“缓存技术”更是后端高并发场景里的基础能力。1. 为什么每个后端开发者都绕不开 Redis1.1 一个用户请求跑得太慢到底慢在哪先回到最原始的场景。没有 Redis 的时候服务器收到一个查看商品详情的请求先查数据库拿到商品信息再查库存可能还要查促销价格最后拼成一个 JSON 返回给前端。流程没错问题出在数据库身上数据最终存在磁盘里磁盘的随机 I/O 性能和内存差了不止一个数量级。当流量上来同一张热表被查询几千次数据库的连接池就容易被占满接口延迟直接飙升。Redis 解决的核心问题非常简单把热数据放到内存里让高频读取不再打到磁盘。你可以把它理解成超市收银台旁边摆放的口香糖和矿泉水——大部分人都有即时需求与其让顾客每次都绕到最里面的货架去拿不如把这些高频商品放在最容易拿到的地方。这就是缓存的直觉逻辑Redis 则是目前最流行的内存容器之一。1.2 缓存之外它其实是个“多面手”很多人以为 Redis 只能做缓存这是个误会。你打开一个在线棋牌游戏的排行榜里面需要实时排序几千人的积分这个场景如果用数据库计算会很痛苦Redis 的 ZSet 结构天然支持分数排序。再比如双十一秒杀时要给库存扣减计数INCR和DECR命令是原子性的单线程执行不会出现并发超卖问题。还有分布式环境下的锁、限流用的计数器、轻量级的发布订阅通知都是 Redis 非常成熟的用途。不过我得泼一盆冷水Redis 能做的事多不代表你应该事事都用它。它毕竟不是关系型数据库不支持复杂的关联查询和事务回滚它也不是消息队列的完美替代品消息可靠性远不如专业的 MQ 中间件。判断标准只有一个这个数据是不是高频读写、是不是能容忍最终一致、是不是不需要特别复杂的计算。满足这三点放进 Redis 往往很合适。1.3 零基础的学习路径这样排最省时间经常有刚入行的朋友问我Redis 那么多命令总不能全背下来吧我的建议是先学怎么“用起来”再学“原理”最后学“踩坑”。第一阶段搞定安装、连接、五种数据结构的基本命令第二阶段搞懂过期时间、内存淘汰策略和持久化机制第三阶段接触缓存穿透、击穿、雪崩和分布式锁这些真正影响线上稳定性的问题。如果你想面试最后的八股文环节也不会脱离这几个方向。这篇文章后面的章节基本就是按这条路径铺开的。2. 从零到能用Redis 安装、连接与可视化客户端实战2.1 Linux 和 macOS 下最简单的安装方式绝大多数生产服务器是 Linux所以第一件事是把 Redis 装在一个类 Unix 环境里。在 Ubuntu 或 Debian 上直接用系统自带的包管理器最快sudo apt update sudo apt install redis-server -y装好之后检查版本redis-server --version用systemctl启动并设置开机自启sudo systemctl enable redis-server sudo systemctl start redis-server如果你用的是 macOS而且装了 Homebrew一行命令就搞定brew install redis brew services start redisbrew services会把 Redis 注册成后台服务重启电脑后也会自动起来对本地开发来说非常省心。安装完成后验证服务是不是正常运行redis-cli ping如果返回PONG说明服务已经跑起来了可以开始用了。2.2 Windows 用户不要硬编译学会这两条路Redis 官方其实不提供 Windows 版本的安装包因为官方更推荐在 Linux 和 macOS 上使用。但 Windows 开发者又确实需要本地环境这时候我建议优先考虑 WSL 方案。在管理员身份的终端里执行wsl --install -d Ubuntu然后在 WSL 里面走 Linux 安装流程这样你接触到的环境更接近生产服务器后续学习主从、集群也不会有太大偏差。如果你实在不想装 WSL想直接体验 Windows 原生版可以找第三方社区维护的 Windows 移植版本解压后看目录下的redis-server.exe双击启动或用命令行启动默认端口 6379。不过需要提醒一句社区移植版的更新速度通常落后于官方版本而且有些扩展模块在 Windows 上表现并不稳定临时学习可以生产环境不要用。2.3 推荐都用 Docker 方式部署干净、可控、可复现如果你手上有 Docker我强烈推荐直接用 Docker。优点是干净不污染本机环境换机器也能快速复现。最基础的启动命令docker run -d --name redis-dev \ -p 6379:6379 \ --restart always \ redis:7.2-alpine如果希望 Redis 开启 AOF 持久化可以在命令里直接追加参数docker run -d --name redis-dev \ -p 6379:6379 \ -v redis-data:/data \ --restart always \ redis:7.2-alpine \ redis-server --appendonly yes把redis-data这个数据卷挂载到容器里的/data即使容器删了重建数据也不会丢。redis-server --appendonly yes会覆盖镜像默认配置启动时直接打开 AOF 持久化。验证连接还是老办法docker exec -it redis-dev redis-cli ping我在本地实践里最常用的是redis:7.2-alpine这个镜像体积小、启动快、调试方便。记得在生产环境不要图省事把端口直接暴露到公网Redis 自身的安全控制能力有限至少要加密码或者绑内网 IP。2.4 可视化客户端怎么选别在工具上折腾太多命令行redis-cli是基础但查数据看类型时总归不如可视化工具直观。社区里常见的几款工具我自己的使用感受是这样的工具适合场景特点Redis Insight官方出品版本更新快界面现代支持命令调试适合学习和排查Another Redis Desktop Manager跨平台、免费、轻量日常开发查 key 看 value 效率高命令行 redis-cli服务器上排障最可靠不会因为图形客户端远程连接出问题连接时最重要的信息是主机地址、端口默认 6379、密码如果有。生产环境里我一般不会直接用可视化客户端连线上库特别是敏感业务环境线上排障优先用跳板机和命令行避免在本地客户端里把数据暴露出来。2.5 Windows 设置开机自启的细节如果是 Windows 原生 Redis希望它开机自启最简单的办法是注册成 Windows 服务。用管理员身份打开终端在 Redis 解压目录执行redis-server --service-install redis.windows-service.conf --service-name Redis redis-server --service-start --service-name Redis后续想卸载服务redis-server --service-uninstall --service-name Redis其实我更推荐用 WSL 启动 Redis再配合 WSL 的.bashrc或 systemd 来管理比 Windows 原生服务更接近真实服务器操作习惯。3. 五种数据结构怎么记才不白学命令、场景和反例3.1 String最基础也是最容易忽略细节的String 是 Redis 里最简单的结构一个 key 对应一个字符串值。但它不只是“存文本”Redis 内部会把它当成字节数组所以数值加减也能直接在内存里完成。常用命令SET user:1001 tom GET user:1001 INCR article:view:888 EXPIRE code:login:1001 120 SETEX token:1001 3600 a1b2c3业务场景最常见的有三种缓存用户信息或配置项、做计数器比如文章阅读数、点赞数、存手机验证码配合过期时间自动失效。你可能会觉得 String 太简单但很多线上事故恰恰是 String 没设计好 key 格式造成的。比如同一个 key 被多个业务共用一个服务写用户昵称另一个服务写用户积分会造成类型冲突或值覆盖。我的实践原则是给 key 设计清晰的前缀例如业务域:对象类型:ID:字段这样既好排查也降低碰撞概率。3.2 Hash最贴合“对象”这个概念的容器如果想把一个用户的多个字段放在同一个 key 下Hash 是最合适的。它相当于 Redis 里的小对象底层是一个字符串字段到字符串值的映射表。常用命令HSET user:1001 name tom age 18 HGET user:1001 name HGETALL user:1001 HDEL user:1001 age用 Hash 存用户资料有几个好处想取某个字段时不用先把整个 JSON 取出来反序列化修改单个字段时只更新局部不会出现“并发覆盖整条记录”的问题。不过要注意 Hash 里的字段不要放得太多太大一个大 Hash 在删除或过期时可能阻塞 Redis 主线程。如果一个对象的字段超过几百个、每个字段又动辄几百 KB那就要考虑做更细粒度的拆分。3.3 List适合时间线、评论列表和轻量队列List 在 Redis 里的本质是一个双向链表或者快速列表可以从左边推入也可以从右边弹出。它天然适合“按时间倒序展示”的场景比如用户的消息通知、系统动态流。常用命令LPUSH feed:user:1001 message-1 RPUSH feed:user:1001 message-2 LRANGE feed:user:1001 0 9 LPOP feed:user:1001这里有个常见的“优先用哪个方向”问题如果你要做一个“最新消息列表”每次都往表头插入用LPUSH如果你要模拟先进先出的队列从右边推入、从左边弹出也就是RPUSH配LPOP的组合。还要注意LRANGE的结尾索引是包含的LRANGE key 0 9会返回 10 条数据写错很容易多取一条或少取一条。List 做消息队列在超低并发下能用但别指望它像专业 MQ 那样可靠消费者挂掉消息就丢了。3.4 Set去重和集合运算的利器Set 是一个无序的、元素不可重复的集合。它的优势在于高效的集合运算比如交集、并集、差集。常用命令SADD tags:1001 java redis SMEMBERS tags:1001 SISMEMBER tags:1001 java SINTER tag:java tag:redis给文章打标签、记录用户关注了哪些博主、统计签到用户都很适合用 Set。很多人第一次上手会疑问我只要能判断元素存在用 Set 还是 String 都行但 Set 的SISMEMBER是 O(1) 复杂度而且做交集并集非常方便。比如你想找出“同时关注了 A 和 B 的用户”一个SINTER user:a:followers user:b:followers就直接出结果用关系型数据库反而要写一堆 join 和临时表。3.5 ZSet排行榜和优先级队列的正确打开方式ZSet 在 Set 的基础上加了一个 score 分数元素会按照 score 从小到大排列。它是我在生产环境里用得非常多的结构几乎所有“需要排序”的需求都能靠它解决。常用命令ZADD leaderboard 100 player-1 95 player-2 ZRANGE leaderboard 0 -1 WITHSCORES ZREVRANGE leaderboard 0 9 WITHSCORES ZSCORE leaderboard player-1 ZINCRBY leaderboard 5 player-1经典场景是游戏排行榜每次用户积分变化时用ZINCRBY在源头上累加分数排名随时可以查。注意 ZSet 里的 score 是 double 类型如果你的积分超过 2^53 的精度范围就不要再依赖它做精确统计否则会出现精度丢失的诡异 Bug。排行榜每天都要重置的话可以给 key 加上日期后缀比如leaderboard:20241120过期时间自然也更好管理。3.6 通用命令、过期时间与“千万别在生产用 KEYS”无论用哪种结构有几个通用命令都可以背下来EXPIRE设置过期时间、TTL查看剩余秒数、TYPE查看 key 类型、DEL删除 key、SCAN遍历 key。这里尤其要提醒一句生产环境不要用KEYS *。它会一次性扫描所有 key数据量大时直接卡住 Redis 主线程导致线上服务超时。我以前排查一个 Redis 节点 CPU 突然飙升的问题最后原因就是有个同事在脚本里用KEYS *清理前缀 key一个千万级 key 的实例直接阻塞了几秒。正确的替代方案是SCAN它一次只拿一小部分数据通过游标循环遍历不会抢住主线程太长时间。3.7 内存淘汰策略当 Redis 满了怎么办很多人以为 Redis 只是存数据不关心容量直到线上出现OOM command not allowed when used memory maxmemory才意识到问题。Redis 支持配置内存淘汰策略在 redis.conf 里有一个关键配置maxmemory 512mb maxmemory-policy allkeys-lru常用的策略策略行为适用场景volatile-lru从过期 key 中淘汰最久未使用保留未设置过期时间的 keyallkeys-lru从全部 key 中淘汰最久未使用纯缓存场景volatile-ttl从过期 key 中淘汰剩余时间最短有明确过期需求noeviction内存满后拒绝写入并报错不可丢数据场景需要特别说明allkeys-lru并不适合所有业务如果缓存里存的是未登录用户信息这类低价值数据可能会把有价值的 key 淘汰掉。生产环境我建议先把 maxmemory 设到一个合理值比如节点内存的 60% 到 70%再根据业务是否允许丢数据来选择策略线上观察一段时间后再调整。4. 缓存真正上线后穿透、击穿、雪崩与分布式锁拆解4.1 缓存穿透查询一个根本不存在的数据缓存穿透指的是请求的数据在缓存和数据库里都不存在此时缓存起不到拦截作用每个请求都会打到数据库。比如恶意攻击者不断请求user:-1这样的无效 ID系统每次都要去数据库查一次数据库压力瞬间被打满。解决思路有两个。第一个是缓存空值查询数据库发现结果为空时在 Redis 里写一个占位字符串同时设置一个较短的过期时间比如 60 秒。这样后续同样的无效查询会被缓存挡住。第二个是布隆过滤器在请求进入系统之前用布隆过滤器快速判断 ID 是否“可能存在”如果过滤器判断不存在直接短路返回根本不需要访问 Redis 和数据库。我在实际项目中更常用“缓存空值 提前校验参数”的组合。原因很简单布隆过滤器有一定误判率实现起来也需要维护一个额外的存储结构而参数校验和空值缓存对大多数业务已经足够。如果面对的真的是超大流量攻击再考虑引入布隆过滤器。一个简单的伪代码演示public String getUser(String userId) { // 1. 先查缓存 String value redis.get(user: userId); if (value ! null) { if (EMPTY.equals(value)) { return null; } return value; } // 2. 缓存没有再查数据库 User user userDao.select(userId); if (user null) { redis.setex(user: userId, 60, EMPTY); return null; } String json JSON.toJSONString(user); // 3. 回填缓存设置合理过期时间 redis.setex(user: userId, 3600, json); return json; }4.2 缓存击穿热点 key 刚过期的那一秒钟缓存击穿和穿透很像但区别在于这个 key 是真实存在的高频热点 key比如秒杀页面的库存信息、首页的头条推荐。当这个 key 过期的那一刻大量请求同时发现缓存没有了于是一起冲向数据库数据库连接被打爆。解决缓存击穿的标准方案是互斥锁重建缓存。请求发现缓存不存在时不是直接回去查数据库而是先尝试获取分布式锁拿到锁的线程负责查库并回填缓存其他线程短暂等待后重新从缓存读取。还有一种思路是“逻辑过期”不设置真正的过期时间而是在 value 里额外存一个过期时间字段后台异步线程发现快过期时自动更新。这种方式能保证前端请求始终不会打到数据库但对数据一致性要求更高的场景要慎用。我自己的经验是如果系统并发高到“热点 key 过期会造成雪崩”很多情况下说明这个热点访问太集中可以考虑在业务层面做流量整形同时在运维层面提前预热热点 key让它根本不要在业务高峰期自然过期。4.3 缓存雪崩大批 key 在同一时间失效雪崩的规模通常大于击穿。比如你给所有商品缓存设置了统一的 1 小时过期时间某个整点时刻几千个 key 同时过期数据库瞬间承担所有读流量就像一个集中爆破点直接压垮底层。最简单的缓解手段是“过期时间加随机偏移”。在设置过期时间时不要用固定值而是加上一个随机数redis.setex(key, 3600 RandomUtil.randomInt(0, 600), value);这样一批 key 的过期时间会散落到一个区间内不再集中在同一时刻。另一个思路是缓存不设置固定过期而是在 value 内部维护更新时间由异步任务统一刷新让“集中过期”变为“分散更新”。如果公司有条件还可以做多级缓存Redis 之上再挡一层本地进程缓存 Caffeine即使 Redis 挂掉或全部过期本地缓存也还能扛住一部分流量。4.4 分布式锁为什么不能用简单的“先占坑后删除”分布式锁是 Redis 在微服务场景里最常被问到的点。比如多个服务实例同时处理同一笔订单的退款如果没有锁可能出现重复退款。Redis 实现分布式锁的基础命令是SET lock:order:1001 uuid-123 PX 30000 NX这个命令同时具备了“如果不存在才设置”“设置过期时间”两个语义比以前的SETNX加EXPIRE分开执行安全得多因为后者在中间一步失败时会导致锁没有过期时间。拿到锁之后释放锁也不是随手一个DEL而是要保证“删的是自己的锁”。如果线程 A 的锁由于处理时间太长已经自动过期线程 B 重新获取到锁此时 A 再执行DEL会把 B 的锁删掉。正确做法是用 Lua 脚本比较锁里的 value 是否一致if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end生产项目里我更推荐直接使用 Redisson 这类封装好的客户端它会自动处理锁续期问题也就是所谓的“看门狗”机制避免业务没执行完锁就过期了。不过要记住单机 Redis 的锁并不适合高可靠的分布式场景真正需要强一致的话需要考虑红锁方案或者引入 etcd、ZooKeeper 这类具备共识能力的组件。4.5 序列化策略为什么 Redis 里存了一堆乱码用过 Spring Data Redis 的朋友应该见过这种场景用 Redis 桌面客户端一打开key 前面多了一长串十六进制字符\xAC\xED\x00\x05value 也完全看不懂。这通常是因为使用了默认的 JDK 序列化器虽然 Java 对象能存进去但可读性和跨语言兼容性都很差而且序列化后的体积明显偏大。我的建议是如果缓存的数据需要让非 Java 服务也看得懂使用统一的 JSON 序列化方案比如GenericJackson2JsonRedisSerializer如果只是简单的字符串键值对用StringRedisTemplate就够了不要为所有缓存对象都引入复杂序列化。序列化本身的坑很隐蔽——一旦上线了一个序列化方案后续如果想换线上所有 key 都需要平滑迁移否则老数据读不出来。所以一开始就要根据业务形态选好序列化策略。5. 断电不丢数据RDB 与 AOF 持久化机制选型5.1 RDB 快照定期把内存数据“拍张照”RDB 持久化会在指定的时间间隔内生成当前 Redis 数据的二进制快照文件默认文件名为dump.rdb。它像一个照相机每隔一段时间拍下整个内存数据的快照恢复时直接载入这张照片。Redis 默认配置已经有几条触发规则save 900 1 save 300 10 save 60 10000意思是900 秒内有至少 1 个 key 变化则保存一次300 秒内有至少 10 个 key 变化则保存一次60 秒内有至少 10000 个 key 变化则保存一次。除了自动触发你还可以在需要时手动执行BGSAVE它会 fork 出一个子进程去做快照主线程继续处理命令。RDB 的优点很明显文件紧凑恢复速度快非常适合做全量备份。缺点也很明确如果 Redis 在两次快照之间意外宕机最后一次快照之后写入的所有数据都会丢失。最多可能丢多少如果你按照save 900 1配置理论上可能丢长达 15 分钟的数据。对重要业务来说这个损失通常不可接受。5.2 AOF把每次写命令记录下来像“账本”一样回放AOF 持久化记录的是每个写操作命令本身而不是数据快照。Redis 启动时可以重新执行这些写命令来恢复数据。默认的 AOF 同步策略是everysec也就是每秒把日志缓冲区写入磁盘一次极端情况下最多丢失这一秒内的写操作。appendonly yes appendfsync everysecappendfsync有三个选项选项行为数据安全性always每个写命令都同步磁盘最安全但性能损耗大everysec每秒异步同步一次折中方案生产常用no由操作系统决定何时落盘性能最好可能丢更多数据AOF 文件如果一直增长会越来越大。Redis 提供了 AOF 重写机制它会对现有数据生成一组最小命令集合丢弃重复的历史记录把文件压缩到一个较小规模。在生产环境里AOF 重写是常见的主线程阻塞来源之一需要在流量低峰期安排并合理配置 fork 内存和硬盘 IO。5.3 生产环境到底怎么选混合持久化才是常态很多人把 RDB 和 AOF 当成二选一但官方和社区的主流实践是两个都开。RDB 负责快速恢复和数据备份AOF 负责尽量少丢数据。Redis 4.0 之后已经支持混合持久化AOF 重写时会把 RDB 快照作为文件头再追加重写期间的新写命令这样加载速度既快数据又更完整。在 redis.conf 里开启混合持久化只需要aof-use-rdb-preamble yes我自己的实践建议是开发环境全关持久化都可以反正数据丢了不心疼测试环境建议打开 AOF方便排查问题生产环境必须开 AOF RDB 混合同时每天在低峰期做一次 RDB 备份备份文件复制到独立存储次数不要只留一份。如果你只把 Redis 当缓存持久化似乎可有可无但请记住缓存数据一旦都是通过复杂计算得到的丢了再重建也一样要付出巨大成本。5.4 实操如何手动触发备份和验证文件手动触发 RDB 快照redis-cli bgsave执行后可以在 Redis 的数据目录看到dump.rdb文件文件修改时间说明刚生成。想确认 AOF 文件是否完整可以用 Redis 自带的检查工具redis-check-aof --fix appendonly.aof如果是 Docker 容器数据卷路径一般在/data/dump.rdb可以直接进入容器查看docker exec -it redis-dev ls -lh /data不要把备份文件只放在本机同一个磁盘否则服务器磁盘损坏就是“跟 Redis 一起殉葬”。我在生产环境里习惯把 RDB 备份同步到对象存储或远程备份服务器这样即使节点被清空也能从备份中恢复绝大部分数据。6. 从单机到高可用主从复制、哨兵与 Redis Cluster 的演进逻辑6.1 为什么单机 Redis 不够用演进脉络先理清单机 Redis 有两个天花板一是所有读写都集中在一台机器上性能有上限二是这台机器一旦宕机所有依赖它的服务都受影响。为了突破这两个限制Redis 出现了三层演进主从复制解决“数据有副本”的问题哨兵解决“故障自动切换”的问题Cluster 解决“数据横向扩容”的问题。记住这套演进逻辑比死记技术名词有用得多。从单机到主从相当于给数据做了两份拷贝从主从到哨兵相当于给主从架构加了一个自动巡检员从哨兵到 Cluster相当于把数据分片存到多组主从中让容量和吞吐可以水平扩展。6.2 用 Docker 快速搭一套主从复制主从复制几乎是学习高可用的必经之路。我习惯用 Docker 在一个节点上模拟出一主两从的拓扑命令如下。先创建一个自定义网络docker network create redis-net启动主节点容器名直接作为从节点连接时的主机名docker run -d --name redis-master \ --network redis-net \ -p 6380:6379 \ redis:7.2-alpine启动第一个从节点并通过--replicaof指定主节点地址docker run -d --name redis-slave1 \ --network redis-net \ -p 6381:6379 \ redis:7.2-alpine \ redis-server --replicaof redis-master 6379验证主从是否同步成功redis-cli -p 6381 info replication看master_link_status:up就说明从节点已经跟上主节点了。这时候你在主节点写入一个 keyredis-cli -p 6380 set name redis-master redis-cli -p 6381 get name从节点能读到相同的 value说明复制链路已经打通。主从复制在默认配置下是异步复制主节点写入成功后立刻返回不等待从节点确认所以主节点突然宕机时从节点可能落后几条数据这也是很多强一致场景对 Redis 感到头疼的原因。6.3 哨兵在主从后面加一个“自动巡检员”主从复制能提供数据副本但当主节点宕机时从节点不会自动顶上需要人工干预。哨兵Sentinel就是来解决这个问题的。它独立于 Redis 数据节点运行监控各个主从节点的健康状态当主节点失联时哨兵会通过投票选出一个从节点提升为新的主节点并把其他从节点重新指向新主。哨兵部署的三个关键点一是哨兵自身至少部署三个以上实例并形成奇数才能避免在“脑裂”情况下产生错误的故障转移二是哨兵之间也是分布式系统它们互相确认主节点是否真的不可用一般配置quorum为多数三是客户端需要接入哨兵地址而不是直接连主节点这样才能自动感知主节点切换。如果你在 K8s 里部署 Redis很多云厂商和开源社区也有成熟的 Operator 可以处理哨兵和哨兵配置不需要自己手动维护三套配置文件但我们仍然要理解背后的流程否则出问题根本不知道日志里为什么出现failover。6.4 Redis Cluster数据分片与横向扩容主从加哨兵解决了高可用问题但没有解决容量和写入性能瓶颈。Redis Cluster 把数据分散到多个主节点每个主节点只负责一部分 keyslot 槽位数量固定为 16384 个。当一个 key 需要写入时Redis 会通过 CRC16 算法计算它属于哪个槽再定位到对应的主节点。所以 Cluster 的最小规模通常建议 3 个主节点再为每个主节点配一个从节点形成 3 主 3 从的架构。手动搭建 Cluster 比主从要繁琐一些需要准备 6 个配置文件、开启cluster-enabled yes、正确设置每个节点的端口再执行redis-cli --cluster create进行握手和分配槽位。如果只是本地学习建议直接使用 Docker Compose 一次性拉起一套集群但重点还是理解槽位机制和客户端路由规则。Spring Data Redis 连接 Cluster 时配置的节点地址可以写多个客户端会自动处理重定向请求这里需要注意连接超时和命令重试策略。6.5 我一个“只做缓存”的玩家需要上集群吗这是个好问题。我的回答是看数据量和可用性要求。业务初期单机 Redis 加主从复制完全够用缓存即使丢失一部分数据库还能兜底。真正要上 Cluster通常是内存容量已经超过单机内存压力、写入 QPS 超过单实例处理能力、或者业务决定了缓存不能整体不可用。单纯为了“用集群看起来很专业”而引入 Cluster只会增加运维成本slot 迁移、跨节点的事务限制、客户端路由复杂度每一项都是需要时间去填的坑。7. 平时没人教的上线后运维与排错清单7.1 最常见的几种“运行中才会遇到”的报错**Redis command timed out。**如果你在 Spring Boot 日志里看到类似io.lettuce.core.RedisCommandTimeoutException: Command timed out第一反应先别怪网络。自己先拆解几个点Redis 实例 CPU 是否被打满、是否存在大 key 操作、客户端连接池是否被占满、命令重试是否导致叠加压力。我碰到的多次超时里一半以上原因是某个大 key 在华丽的DEL或KEYS操作中阻塞了 Redis 主线程。**Docker 搜索 redis 时报 500。**如果你执行docker search redis出现request returned 500 internal server error先去确认 Docker Desktop 引擎是否真的启动成功再检查磁盘空间是否不足。很多时候只是 Docker Desktop 刚启动还没就绪等几十秒再执行docker info确认状态即可。这不算 Redis 本身的问题但很容易把新手吓一跳。**Windows 下安装 Redis 提示缺少 Visual C 依赖。**社区版 Windows Redis 的部分版本依赖微软运行库报错时去安装对应的 Visual C Redistributable 即可。这类问题属于环境依赖不是代码问题。7.2 大 key 和热 key 是两大隐形杀手“大 key”通常指单个 key 的数据量过大比如一个 Hash 里有上百万字段一个 List 里有几百万条数据。大 key 删除时会引发长时间内存释放和复制风暴遇到请求量高的时候甚至可能让 Redis 长时间无响应。排查方法很简单Redis 自带命令redis-cli --bigkeys它会按类型扫描找出比较大的 key。但注意--bigkeys本身也是遍历全库最好在流量低峰期执行或者结合SLOWLOG判断是否存在慢命令。“热 key”则是某一两个 key 的访问量极高把 Redis 的单节点 CPU 打到极限。实际项目里可以先从慢查询日志和访问侧统计发现必要时把热 key 数据在集群多副本之间做本地缓存分散。遇到过几次热 key 问题后我的一个经验是别想着靠 Redis 内部配置解决所有热点业务侧加一层短时本地缓存往往立竿见影。7.3 日志与监控不要等告警了才开始看Redis 不像应用服务那样每天打印大量日志但关键信息还是有的。Docker 部署时查看日志很直接docker logs redis-devLinux 本机部署时日志一般在/var/log/redis/redis-server.logmacOS 用brew services info redis可以看到日志位置。生产环境里我觉得有两个指标比“当前内存多少”更重要一是instantaneous_ops_per_sec体现 Redis 当前吞吐二是connected_clients如果客户端连接数异常增长通常说明业务侧连接池泄漏。定期执行redis-cli info看关键指标比出事后再翻日志更靠谱。7.4 面试里 Redis 常被问到的几个点怎么回答才不虚网上“Redis 面试八股文”很多但我不建议背答案理解底层逻辑再回答才有底气。比如“Redis 为什么快”三个主要原因完全基于内存操作、命令执行采用单线程模型避免了锁竞争和上下文切换、底层使用 IO 多路复用处理网络连接。新版 Redis 在 IO 读写上引入了多线程但核心命令执行依然是单线程这个细节说清楚就能和单纯背答案的人拉开差距。“缓存一致性”也是一个高频题。合理的思路是更新数据库之后删除缓存而不是先更新缓存如果担心删除缓存失败导致脏数据可以用延迟双删先删缓存、更新数据库、稍后再删一次缓存。当然延迟双删本身也不是银弹它会增加系统复杂度所以实践中要结合业务对一致性容忍度来选择方案。“淘汰策略”的题目比如 LRU 和 LFU 的区别要说出本质LRU 按最近访问时间淘汰LFU 按访问频率淘汰。LFU 能更好抵挡“冷 key 突然被访问一次”导致的热点置换但实现也更复杂。Redis 默认的allkeys-lru在大多数缓存场景已经够用不过如果业务里有明显的频次差异我会选择allkeys-lfu。7.5 给初始化第一期的小白几个最好用的习惯最后说几个我自己的日常习惯。第一所有 key 一定要设置过期时间除非你非常清楚它真的不该过期否则一堆永不过期的 key 会慢慢推高内存。第二给所有 key 加统一前缀小组内部最好有一份命名规范。第三上线前先估算最坏情况下的内存使用量直接把maxmemory配置到位不要等 Redis 写满报错后再手忙脚乱。第四任何删除大 key 的操作之前先看DEBUG OBJECT key或MEMORY USAGE key了解它的真实占用提前想好是分批删除还是直接修改过期时间。Redis 这个工具说实话不难真正难的是“长期稳定运行不出事”。我踩过不少坑比如在高峰期用KEYS *扫描业务前缀、用固定过期时间造成缓存雪崩、用 JDK 默认序列化导致线上数据全部变成乱码。这些坑如果你在入门阶段就意识到后面能少熬很多夜。希望这篇实战指南能让你从第一行redis-cli ping开始稳稳走进缓存技术的大门。
返回列表