
做后端这些年我见过太多同学把Redis的命令背得滚瓜烂熟面试时String、List、Hash都能说上几句可真到了生产环境一压测要么缓存穿透把数据库打挂要么AOF文件膨胀到几十个G要么主从切换丢了几秒数据还在那儿一脸懵。这篇Redis学习总结我不打算给你罗列官方文档式的命令清单而是按一个完整的学习路径来写先说清Redis到底解决了什么问题再拆它的底层设计、五种核心数据结构、持久化机制、缓存三兄弟、内存管理、高可用架构最后是常见故障排查实录。每个部分都会补充我实际踩坑后的心得希望能帮你把这块真正学透。这套内容适合三类人刚接触Redis的新手想要系统梳理知识体系的中级开发以及需要排查线上问题、做架构选型的运维和架构师。你不需要一次性读完把它当作一份随时可以查阅的手册就行。1. 先建立全局认知Redis到底是干什么的1.1 一句话理解Redis的定位Redis的全称是Remote Dictionary Server本质上是一个基于内存的键值数据库。很多人把它当成“缓存”其实它远不止缓存官方更愿意叫它数据结构服务器。区别在哪里普通KV存储像一个大箱子你只能往里放整个对象Redis更像一整排抽屉每种数据类型都有自己的操作方法可以精确操作里面的字段、成员、排名。我通常这样给新人解释MySQL负责“存得住”Redis负责“取得快”。数据库的数据在磁盘上每次查询都要走一次磁盘IORedis的数据在内存里读写在纳秒到微秒级完成单机可以轻松扛住十万级QPS。于是典型的架构就是把热点数据提前放进Redis让80%的读请求打在内存上数据库只承担写和兜底。1.2 先想明白Redis能解决什么问题学习任何技术前先问“它是为哪个场景设计的”比直接背命令重要得多。Redis的核心能力可以归纳成四件事加速读写把热点数据放进内存降低数据库压力提升接口响应速度。数据结构服务利用List做消息队列、利用ZSet做排行榜、利用Set做去重和交集并集运算、利用Hash管理对象字段。分布式协调基于Redis实现分布式锁、分布式限流、Session共享等很多组件都依赖它的原子命令。数据暂存与缓冲比如秒杀场景中的库存预扣减、异步任务队列、实时榜单等。有一个误解必须先纠正Redis不是万能的缓存不是所有数据都适合放Redis。数据量巨大且访问频率低的冷数据放进去是浪费内存需要复杂事务和关系查询的数据Redis做起来很别扭持久化要求极高、不能丢任何一条记录的场景纯Redis做主存储也要小心。搞清楚边界才能选对方案。1.3 整个知识体系的框架图我在带新人时通常会让他们先在心里挂一张“Redis知识地图”这样后续学到的每个点都能挂到对应位置底层基础数据结构、网络模型、单线程与多线程IO、内存管理。核心功能五种基本类型、三种高级类型、事务、管道、Lua脚本、发布订阅。可靠性RDB持久化、AOF持久化、混合持久化、数据恢复。可用性主从复制、哨兵、集群、容灾。工程实践缓存穿透/击穿/雪崩、缓存一致性、大Key治理、慢查询排查、监控。周边生态客户端选型、Redis和数据库同步工具、可视化管理平台。把这六块都过一遍再去做实战思路会很清晰遇到问题时也能快速定位是哪一层出了问题。2. 底层设计拆解为什么Redis能那么快2.1 快不只是因为内存很多人回答“Redis为什么快”只会说“因为它是内存数据库”这个答案太浅了。它的快来自三件事叠加数据在内存、IO模型高效、底层数据结构经过了深度优化。内存存取确实比磁盘快几个数量级但光有内存不够。如果网络处理模型低效大量请求在IO上排队照样拉胯。Redis把这两件事都做到位了数据存储结构紧凑省内存IO事件处理模型高效省CPU。2.2 单线程模型为什么反而更快Redis的命令处理核心是单线程的。一听到“单线程”很多从并发编程过来的人第一反应是“那岂不是浪费CPU”。实际上Redis的瓶颈从来不是CPU而是网络IO和内存带宽单线程反而带来几个巨大优势天然避免竞态条件不用加锁就能保证命令的原子性这也是Redis适合做计数器、分布式锁的基础。减少线程切换开销多线程编程中上下文切换成本很高单线程内核态切换少反而更可控。调试和运维简单一个线程跑完所有命令线上出问题相对容易复现。性能测试数据也能说明问题单线程Redis处理简单GET/SET命令单机可以达到10万QPS以上很多业务根本用不满。如果未来遇到单线程导致的CPU瓶颈可以引入集群分片而不是在单实例上盲目多开线程。2.3 IO多路复用与事件循环后面的6.0版本虽然加入了多线程IO但要先理解经典模型。Redis基于Reactor模式实现了一个事件循环核心系统调用是epollLinux或kqueueBSD本质是让一个线程同时监听成千上万个客户端连接。用生活里的例子解释单线程Redis像一位高效的前台接待他不会盯着每个客户看而是坐在工位上听到铃声才响应哪个窗口有人呼叫就去处理哪个没人呼叫就休息。传统多线程模型则是给每个客户配一个接待员客户一多就忙不过来还要不断轮换交接。事件循环里主要处理两类事件文件事件客户端读写请求和时间事件定时任务比如过期键清理。Redis把命令处理拆成“读取请求、解析执行、写回响应”三个阶段在单线程里串行完成才能保证所有命令执行的原子性。2.4 底层数据结构是性能的另一半Redis对外提供几种数据类型内部为了兼顾内存和速度同一类型在不同条件下会切换编码结构。这里列一张简化对照表建议收藏外部类型底层编码触发条件简化表述Stringint、embstr、raw整数则存int短字符串用embstr长字符串用rawListquicklist由多个ziplist节点组成的双向链表Hashlistpack新/ ziplist、hashtable字段少且值短时用紧凑结构数据量大后转hashtableSetintset、hashtable全部是整数且数量少时用intset否则用hashtableZSetlistpack新/ ziplist、skiplistdict元素少且值短用紧凑结构量大后用跳表加字典跳表skiplist是Redis的一大特色很多初学者觉得“为什么不直接用一个红黑树”。跳表实现比红黑树简单很多支持按分数范围做范围查询、按排名取元素维护成本也低配合哈希表可以达到O(1)查找成员、O(logN)范围查询的效果。这一层非常值得仔细琢磨面试中几乎必问。2.5 6.0引入多线程IO到底改了什么Redis 6.0之前完全单线程6.0开始把“网络IO的读写”这部分从主线程拆出来用多线程处理但“执行命令”仍然在主线程。原因很实际当客户端一多网络数据包的读写会消耗不少CPU而命令本身很短把网络读写并行化之后并发吞吐量能再上一个台阶。这里有个理解陷阱多线程IO不会让单个命令的执行变快也不会破坏原子性。命令还是在一个主线程里逐条执行多线程只负责解析和回包的“体力活”。所以你在配置Redis时如果QPS本身不高没必要强行调大io-threads调多了反而可能触发CPU争抢。3. 五种基础数据类型从记忆到实战五种类型是整个Redis学习的核心我建议不光记命令还要把它和真实业务场景绑定下面逐个拆。每个类型都给出典型命令组合和场景配合简单示例。3.1 String最基础的键值单元String在Redis内部有多种存储形式如果值是整数直接用int编码这样INCR/DECR就是纯CPU加法和减法短字符串用embstr长字符串用raw。String能存的最大值512MB但生产环境我强烈不建议真往里塞大字符串一个Key几兆就够折腾了。常用命令集中在这些SET、GET、SETNX、SETEX、INCR、DECR、MSET、MGET、APPEND、STRLEN。其中SETNX是分布式锁的原子入口INCR非常适合做计数器比如接口限流、库存扣减、统计PV/UV。一个典型场景是秒杀库存预扣减商品库存先初始化到Redis每次请求走DECR返回值小于0说明库存已尽异步把最终扣减结果同步到数据库。这里要注意一定要配合Lua脚本保证“检查库存-扣减-记录用户”的原子性否则两个请求同时读到库存为1都会执行成功超卖就发生了。提示用SETNX做分布式锁时记得设置过期时间否则客户端宕机会导致死锁。Redis官方给出的是SET key value NX EX seconds这种原子写法不要把SETNX和EXPIRE拆成两条命令。3.2 List双向链表模拟消息队列List底层实现是quicklist由多个紧凑节点组成的双向链表头和尾的操作时间复杂度都是O(1)。常用命令包括LPUSH、RPUSH、LPOP、RPOP、LRANGE、LLEN、BRPOP、BLPOP。第6.2版本引入了Listpack作为新的紧凑编码不过用法上不用感知差异。最经典的用法是做“轻量级消息队列”生产者RPUSH消息消费者LPOP消费。这种方式比专业消息队列比如Kafka、RabbitMQ轻量很多适合对可靠性要求不高的异步任务。如果要阻塞式消费就用BRPOP它会在队列为空时一直等待直到有消息进来或超时和轮询相比可以显著减少无效请求。List还能实现“最新列表”比如用户浏览记录、新闻列表LPUSH之后用LRANGE 0 9取前10条。注意别用LINDEX在长列表里频繁取值它是O(N)操作会把CPU烧上去。List里存的对象如果很大尽量只存ID或序列化后的短字符串不要让单条数据过大。3.3 Hash对象字段管理的天然选项Hash适合表示“一个对象的多个属性”比如用户信息键是用户ID字段是name、age、email值是具体属性。这样修改单个字段时不用像JSON字符串那样把整个对象拿出来反序列化再写回去性能高不少。常用命令HSET、HGET、HGETALL、HMSET、HDEL、HINCRBY、HSCAN。Hash在字段少且值短的时候使用紧凑编码内存占用非常可观。我记得之前把一个电商订单列表从JSON字符串改成Hash存储Redis内存直接下降了40%这个收益在键规模千万级别时相当明显。需要注意HGETALL会把所有字段一股脑返回如果字段很多、每个字段值又大HGETALL会消耗大量内存和带宽。这时候改成HSCAN分批次扫描或者只取需要的字段HGET/HMGET。线上排查大Key时Hash经常是大户字段数量几十万甚至几百万这时候要找出来重新设计存储。3.4 Set无序集合去重与运算Set的特点是元素唯一且无序底层在元素少且为整数时使用intset否则用hashtable。常用命令SADD、SREM、SMEMBERS、SISMEMBER、SCARD、SINTER、SUNION、SDIFF、SPOP、SRANDMEMBER。业务上最常见的是“标签系统”给一个用户打上“程序员”“后端”“足球”三个标签用SADD key member然后可以求两个集合的交集找出同时是“程序员”和“足球”爱好者的用户。另一个经典场景是抽奖所有人ID放进Set用SPOP随机弹出几个作为中奖者天然去重。SISMEMBER判断成员是否存在是O(1)操作比把集合拉到客户端遍历要靠谱。SMEMBERS返回全部元素大集合慎用应该用SSCAN迭代扫描。Set还有一个特殊用途做“关注关系”A关注的人和B关注的人用SDIFF算出“你关注的人中谁没有关注你”这类关系运算很直观。3.5 ZSet排行榜和延迟队列的基石ZSet在Set的基础上给每个元素附加了一个score底层用跳表加字典实现。字典负责按成员找分数O(1)跳表负责按分数排序和范围查询O(logN)。常用命令ZADD、ZRANGE、ZREVRANGE、ZSCORE、ZINCRBY、ZRANGEBYSCORE、ZREM、ZRANK。最典型的场景是排行榜玩家分数用ZINCRBY实时更新展示Top N用ZREVRANGE 0 N-1要查某个用户排第几用ZRANK。这些都是O(logN)或O(MlogN)的操作在千万级成员规模下依然能压到毫秒级。ZSet还能做“延迟队列”时间戳作为score不断用ZRANGEBYSCORE取score小于当前时间的元素取到即认为任务到期处理完ZREM。实现简单还能顺便支持按优先级排序。注意ZSet的score是双精度浮点不要用超过精度范围的整数做大ID否则会出现相等或错乱的问题成员名不要设计过长过期扫描成本也跟元素长度有关。3.6 三种高级类型也要知道除了五类基础类型Redis还提供了Bitmaps、HyperLogLog、Geo。Bitmaps本质是String的位操作适合做签到、在线状态这类海量布尔标记一个用户一天只要一个bit。HyperLogLog用于基数统计比如计算每天UV误差在0.81%左右内存非常省12KB左右就能统计海量数据。Geo主要用于“附近的人”内部基于ZSet实现支持经纬度添加和半径查询。这些类型在特定场景能省下大量内存和开发成本值得花半小时掌握。4. 持久化机制数据不丢才是硬道理Redis是内存数据库进程一重启、机器一宕机内存数据就会灰飞烟灭。持久化的作用就是把内存里的数据落地到磁盘保证重启后能恢复。Redis提供了两种持久化方案RDB和AOF理解它们的原理和取舍是生产环境必备技能。4.1 RDB快照快速但不那么安全RDB会在某个时刻把内存里的全量数据生成一份压缩快照写入磁盘默认文件名dump.rdb。触发方式有两种手动执行SAVE或BGSAVE以及配置自动触发规则比如“900秒内至少有1次写操作”。SAVE是同步操作会阻塞主线程生产环境基本不用BGSAVE则是fork一个子进程由子进程负责写RDB主进程继续服务。这里涉及一个关键知识点fork会复制父进程的页表如果Redis实例内存很大几十GBfork本身可能耗时几百毫秒到一秒多这段时间主进程会短暂阻塞。所以不要让RDB文件变得过大同时确保机器内存充足。RDB的优点是恢复速度快文件紧凑适合做备份和灾难恢复缺点是快照周期之间如果宕机这段时间的写操作会全部丢失。通常RDB多久触发一次就容忍丢多少数据。4.2 AOF日志记录每一次写操作AOFAppend Only File追加记录每个写命令相当于把操作日志原样存下来。恢复时按顺序重放这些命令就能还原数据集。AOF有三种刷盘策略策略行为可靠性性能appendfsync always每次写命令都刷盘最可靠最多丢一条命令最慢磁盘IO压力大appendfsync everysec每秒刷一次盘最多丢1秒数据折中生产最常用appendfsync no交给操作系统决定可能丢较多数据最快这里的“刷盘”是理解AOF可靠性的重点。命令先写入缓冲区再由fsync同步到磁盘。如果中间宕机缓冲区里的数据就会丢。everysec每秒刷一次在绝大多数业务场景下可接受always每次写都刷盘但会严重拖慢写性能通常只有对数据丢失极度敏感的金融类场景才会考虑。AOF还有一个问题是文件会不断膨胀所以Redis提供了AOF重写机制把当前数据集里的数据转换成最精简的命令集合生成新文件并替换旧文件。重写过程也是fork子进程来做主进程的新写命令会先存入重写缓冲区子进程完成后再追加保证不会丢数据。4.3 混合持久化4.0之后的最优解Redis 4.0引入了混合持久化开启后AOF文件头部是RDB格式的全量快照后面再追加增量命令。这样兼顾了RDB的恢复速度和AOF的低数据丢失。开启方式是在redis.conf里设置aof-use-rdb-preamble yes。我的实际经验是新版Redis建议直接使用混合持久化再配合RDB做定期备份。如果数据量很大可以把RDB关闭只保留AOF但要注意AOF文件加载速度会比RDB慢不少。千万别同时把两个都配置得激进比如AOF everysec同时加密集RDB snapshot会产生频繁的fork子进程反而拖垮主进程。4.4 数据恢复和文件修复实操恢复流程其实很无脑启动Redis时如果AOF或RDB文件存在它会自动加载。加载顺序是AOF优先只有当appendonly no且RDB存在时才用RDB。生产环境有几个细节值得注意备份文件多保留几个版本不要只留最新的防止最新文件已经损坏。如果AOF文件尾部因断电损坏启动会失败。Linux上用redis-check-aof工具修复命令是redis-check-aof --fix appendonly.aof它会截断损坏部分恢复剩余数据。RDB文件损坏用redis-check-rdb。启动大Redis实例需要时间AOF重放几GB文件可能耗时几分钟这个时间要规划到发布流程里避免因为启动超时被误判为故障。5. 缓存三兄弟穿透、击穿、雪崩这几乎是Redis面试必考也是线上最容易出的三类事故。很多人背了定义但排查时仍分不清所以我给一套判断口诀穿透是“查了个不存在的东西”击穿是“热点Key过期瞬间”雪崩是“大量Key同时过期或Redis整体崩了”。5.1 缓存穿透请求打到一个不存在的数据场景很常见恶意攻击者或用户不断请求一个缓存和数据库里都不存在的ID比如负数ID、不存在的用户ID。因为缓存没有请求就会一直穿透到数据库数据库压力瞬间拉满甚至被打挂。解决方案有三种。第一种是缓存空值查不到数据时在Redis里放一个空对象并设置较短过期时间比如2~5分钟后续同样请求直接命中空缓存不再打库。缺点是空值也是Key会占用内存且要在数据新增后主动删除空缓存。第二种是布隆过滤器启动时把所有可能存在的ID加载到布隆过滤器请求先过过滤器过滤器说不存在就直接返回404根本不查缓存和库。布隆过滤器的特点是“宁可错杀三千不可放过一个”它说存在才可能去查数据说不存在一定不存在。第三种是参数校验兜底在网关层过滤明显非法的ID比如负数、超长字符串。单用参数校验只能挡住低级的攻击真正要根治线上一般用“缓存空值 布隆过滤器”组合。我见过不少团队只加了空值缓存结果攻击者换一批不存在的ID又把数据库打穿空值缓存内存也暴涨所以过滤器还是有必要。5.2 缓存击穿热点Key过期的瞬间击穿和穿透的差别在一个字击穿针对的是“某一个热点Key”它在缓存过期的那一瞬间大量并发请求全部涌向数据库。典型场景是微博热搜、商品详情页的爆款商品平时缓存命中很高但Key一旦过期所有请求就像约好了似的穿过去。解决方案之一是互斥锁在Key过期时用一个分布式锁比如SETNX让只有一个请求去数据库加载数据并回写缓存其他请求等待锁释放后重新读缓存。这能大幅降低数据库压力但如果实现不当会让请求串行化整体吞吐降低。方案之二是逻辑过期缓存里不设置物理过期时间而是把过期时间作为业务字段写在Value里每次读的时候检查是否过期过期后由后台任务异步刷新。这样查询永不过期数据库压力被异步任务平摊体验更好。方案之三是给热点Key设置更长的过期时间甚至永不过期配合定时任务主动更新。看起来很简单但热点变化很快你需要有一套识别热点的机制。5.3 缓存雪崩大面积Key同时失效雪崩的杀伤力最大。场景有两个层级一是缓存集中过期大量Key在同一时间窗口失效比如批量写入缓存时都设置了相同的过期时间几十万Key在零点集体失效数据库在零点被冲垮二是Redis服务整体宕机所有请求全部落到数据库。针对第一层解决办法是过期时间散列化在统一的过期时间上加一个随机数比如300秒加随机0~60秒让Key的过期时间均匀分布。也可以用多级缓存本地缓存加分布式缓存本地缓存先扛住一层。针对第二层就必须做高可用至少主从加哨兵核心场景上集群同时要做限流和熔断数据库端加保护防止雪崩变成数据库宕机。5.4 三兄弟区分速查表我整理了一张小表遇到问题先看这张表再做方案问题对象范围现象核心原因最直接手段穿透缓存和库都没有请求到达数据库Key不存在布隆过滤器、缓存空值击穿单个热点Key热点Key过期瞬间Key过期互斥锁、逻辑过期雪崩大量Key/整个Redis集中过期或宕机过期时间集中/高可用缺失过期时间随机化、高可用6. 内存管理不让Redis OOM也不让大Key拖垮一切6.1 过期删除策略是怎么工作的Redis里可以给Key设置TTL过期之后Redis并不会立刻把它清掉而是混合使用“惰性删除”和“定期删除”。惰性删除是当Key被访问时才检查是否过期过期就删除并返回空定期删除是后台周期性地抽样一些Key删除其中的过期Key。这个设计的逻辑很简单如果每毫秒都去扫描所有Key删除过期的CPU会吃不消如果只靠惰性删除又会有很多过期Key躺在内存里占地方。于是两者结合牺牲一点点内存换取CPU开销可控。代价是过期Key可能短暂残留在内存里如果大量过期的Key同时存在定期删除扫不过来内存可能被白白占用这时要看INFO stats里的expired_keys指标是否异常。6.2 内存淘汰策略怎么选Redis启动时可以配置最大内存maxmemory当内存用完后再写新数据就会触发内存淘汰策略。默认策略是noeviction即不让写入新数据直接返回OOM错误。绝大多数业务不会这么干所以需要配置淘汰策略。常用的几种策略allkeys-lru从所有Key中淘汰最久未使用的Key。allkeys-lfu从所有Key中淘汰使用频率最低的Key。volatile-lru只从设置了过期时间的Key里淘汰最久未使用的。volatile-ttl从设置了过期时间的Key里淘汰剩余TTL最小的。怎么选单纯做缓存的业务allkeys-lru用的最多因为它不区分缓存还是热数据全部按访问情况淘汰如果有“Key必须永不过期且不能被淘汰”的核心数据建议用volatile开头的策略把过期Key才当淘汰候选。Redis 4.0之后有LFU策略对某些访问频率稳定、周期性突刺的场景更友好避免LRU下热点数据被一次性扫描挤掉。6.3 大Key与热Key治理大Key指单个Key的Value特别大比如一个String几MB一个Hash有几百万字段一个ZSet有千万成员。大Key的危害很直接删除或过期时会阻塞主线程网络传输时占用大量带宽集群扩容迁移时数据倾斜内存碎片和复制缓冲也可能被拖爆。用redis-cli --bigkeys可以快速扫描出大Key的分布线上建议用scan方式手动抽样避免全量扫描阻塞。热Key指某个Key被超高频率访问比如双十一秒杀商品、热门直播间信息。热Key会打满单分片CPU导致整个Redis实例变慢。解决手段有本地缓存多级缓存把热数据再加一层放进程内读写分离让从节点分担读流量热Key复制把同一个逻辑Key复制成多个物理Key分散到集群不同节点。注意千万不要在生产环境直接执行KEYS *去查Key这一步会扫描全库并阻塞主线程几百万Key的实例可能直接卡死。用SCAN cursor [MATCH pattern] [COUNT count]迭代扫描才能真正做到安全。7. 高可用架构主从、哨兵、集群怎么选单机Redis一旦宕机服务就断了。要提升可用性就得引入多实例。从主从复制到哨兵再到集群是一条清晰的演进路径。7.1 主从复制读写分离的基石主从复制让一个主节点Master负责写多个从节点Slave同步数据并负责读。配置很简单从节点执行SLAVEOF host port或配置文件里指定replicaof即可新版本命令改成了REPLICAOF。复制过程分全量同步和增量同步。第一次同步是全量主节点fork子进程生成RDB发送给从节点同时把新写入的命令缓存到复制缓冲区repl_backlogRDB同步完成后把缓存命令再发给从节点从节点追上进度。之后就是增量主节点把写命令持续推送给从节点类似AOF。这里有个常见的坑主从复制的延迟无法完全避免从节点数据永远是接近实时而不是完全一致。如果业务要求强一致比如扣库存绝对不能先读从库必须走主库或加锁。还有从节点配置了replica-read-only yes防止误写入导致数据不一致。7.2 哨兵模式自动故障转移主从复制解决了数据冗余和读写分离但主节点宕机时需要人工把某个从节点提升为主节点这个响应时间太长。哨兵Sentinel就是用来做自动化故障转移的。哨兵是一个独立的Redis进程它做三件事监控主从节点是否存活发现主节点挂了后在从节点中选举新的主节点并完成故障转移把新主节点地址通知给客户端。要注意哨兵本身也需要高可用生产环境一般部署至少3个哨兵节点奇数个配合quorum参数避免误判和脑裂。故障切换过程中Redis客户端会有几秒到十几秒的不可用窗口这段时间请求会失败。所以哨兵模式适合大多数中小型业务但如果你需要“秒级可用性”且数据量巨大、写并发极高通常考虑集群模式。还有一个易踩的坑哨兵的配置要一致不同节点的sentinel monitor配置不一致会导致互相踢主节点触发频繁切换。7.3 集群模式分片与扩展集群Cluster把数据分布在多个主节点上每个主节点负责一部分哈希槽slot总共16384个槽位。写入一个Key时客户端通过一致性哈希算法计算CRC16(key) % 16384决定Key落在哪个槽位也就知道该请求哪个节点。如果客户端直连了错误的节点该节点会返回MOVED重定向客户端需要跳跃过去。集群的好处是水平扩展数据量大了加几个节点重新分槽即可每个节点还可以配一个从节点保证高可用。缺点是使用上有一些限制不支持多Key操作跨槽位比如MGET多个Key不在同一个节点就会失败需要额外设计Lua脚本引用的Key也必须落在同一个槽内。大厂秒杀、社交Feed这类超高QPS的场景一般直接上Cluster。但中小项目没必要一上来就上集群先主从加哨兵撑住等真有容量瓶颈再演进避免运维复杂度过早膨胀。7.4 我的架构选型心得服务可用性要求低的内部系统单机够了一般线上服务我建议起步就是主从复制加哨兵数据落地至少两份主节点有事能自动切数据量大到单机内存扛不住或者写QPS超过单实例上限再上集群。集群的“最小可用”是3主3从最少也要3主节点太多时运维成本和故障半径都会变大。除Redis本身的高可用之外客户端层面也要做“故障感知”。很多降级方案其实是客户端做的比如配置探活、失败快速重试、超时熔断、本地兜底缓存。别把高可用的宝全押在服务器端客户端一点容错能力都没有照样会出事。8. 常见问题与排查实操实录8.1 慢查询定位先看slowlogRedis默认命令处理极快超过一定阈值就算慢查询。在redis.conf里设置slowlog-log-slower-than为微秒值比如10000表示10毫秒。用SLOWLOG GET即可看到慢命令列表SLOWLOG LEN查看数量。慢查询一旦出现大概率是执行了O(N)命令比如KEYS、HGETALL、LRANGE一个超大列表、ZRANGEBYSCORE一次取上万条。排查出慢命令后立刻优化业务逻辑改用SCAN、限制一次取的数量或者拆分存储。8.2 阻塞定位主线程卡住的原因主线程一旦阻塞所有命令的RT都会飙升。Redis最有名的那些阻塞点我挨个列出Fork子进程做RDB快照或AOF重写这个过程可能阻塞几十毫秒到秒级删除一个大KeyDEL一个几GB的String或几百万字段的Hash同步删除会阻塞主线程应该用UNLINK异步删除执行了KEYS、SORT这类全量命令内存碎片过大分配器找不到连续内存时操作变慢。排查工具方面INFO commandstats可以看到每类命令调用次数和耗时分布LATEST LATENCY、SLOWLOG GET能看出具体卡点。真到紧急恢复先把有问题的慢命令和大Key解决再考虑重启盲目重启可能让数据恢复阶段阻塞更久。8.3 连接数异常TIME_WAIT和maxclients客户端连接打满页面报“max number of clients reached”多半是客户端没有正确关闭连接或连接池配置过大且长期空闲。在服务端redis-cli info clients查看connected_clients在应用层排查实例的TIME_WAIT和CLOSE_WAIT状态CLOSE_WAIT异常通常是业务代码里连接没有close。常见解法配置连接池最大空闲连接数设置空闲回收时间客户端请求使用短连接时改用连接池如果短连接特别多在操作系统层调整tcp_tw_reuse和tcp_max_tw_buckets。值得注意的是盲目调大maxclients并不能解决根因只会推迟问题爆发。8.4 缓存与数据库不一致怎么办这是个老生常谈的问题。主流方案是“Cache Aside”读的时候先读缓存没有则读数据库并回写写的时候先更新数据库再删缓存或更新缓存。但“先更新数据库再删缓存”存在并发窗口在极端情况下一个线程读旧值回写缓存另一个线程更新数据库再删缓存可能因为删除顺序问题导致短暂不一致。实践中更稳妥的是延迟双删更新数据库后先删缓存过几百毫秒再删一次或者在缓存更新失败时用MQ异步重试删除。Redis 6.x以后缓存回写可以用客户端配合类似协议机制不过现在大多数业务用“过期时间作为最终兜底”就够了。记住一致性要求极高的数据就别缓存这是最省心的方案。8.5 故障排查工具箱速查场景排查命令关键指标看整体状态redis-cli infoused_memory、connected_clients、instantaneous_ops_per_sec慢命令redis-cli slowlog get命令名、耗时、参数大Keyredis-cli --bigkeys各个类型最大的Key阻塞事件redis-cli latest latency最近的事件耗时内存碎片redis-cli info memorymem_fragmentation_ratio持久化状态redis-cli info persistencerdb_last_bgsave_status、aof_last_write_status遇到问题先别慌按“连接数-内存-CPU-命令延时-持久化状态”的顺序逐层筛。最近一次线上抖动我先看connected_clients没异常再看INFO stats里的sync_full数量最后定位到是主从复制频繁全量同步导致主节点CPU飙高。能用监控曲线解决的比拍脑袋猜靠谱得多。9. 学习路线建议从零到写进简历据我个人的带人和自学经验Redis的系统学习可以分成三个阶段每个阶段做的事不太一样。9.1 第一阶段命令和数据类型先把五种基本类型和三种高级类型常用命令过一遍不用背但要知道“什么问题用什么类型”。自己搭一个单机Redis在redis-cli里敲一遍命令然后设计几个小任务用String实现计数器、用List实现消息队列、用Set实现关注关系、用ZSet实现排行榜。这个阶段目标是能写出正确的代码理解数据结构在业务中的位置。不用纠结底层原理但至少要知道“为什么ZSet适合排行榜却不适合全文搜索”。9.2 第二阶段可靠性和一致性做一个涉及持久化的项目观察RDB和AOF的触发、恢复过程用kill -9模拟宕机看看数据丢了多少。再做一个带缓存的项目主动制造缓存穿透和缓存击穿分别上用缓存空值、互斥锁、逻辑过期方案去修复。这个阶段是最能积累经验的别怕线上踩坑坑踩得越多对Redis越有体感。顺便把分布式锁做一遍用Redis实现一个带过期续期功能的小工具理解Redisson那套看门狗背后的逻辑。9.3 第三阶段架构和运维把单机扩展成主从加哨兵再搭一个3主3从的Cluster。做一次故障演练手动把主节点宕掉观察哨兵切换时间在Cluster上写入不同Key观察槽位分配和MOVED重定向。这个阶段要开始看Redis源码里的基础数据结构和网络模型比如SDS、listpack、跳表、epoll事件循环。源码不一定要全读完但核心数据结构、过期机制、持久化实现要能讲清楚面试基本都是问这些。最后再分享一点个人体会。我见过太多人收藏了一堆“Redis最全命令大全”却一直停留在“收藏等于学会”真正拉开差距的是能不能讲清楚为什么这个数据结构省内存、为什么这个场景会被Redis打垮、为什么切换主从时会先选这个从节点。学到后来你会意识到命令是最不值钱的值钱的是对数据模型的理解、对性能边界的感知、对故障规律的敬畏。每次上线前我都习惯问自己三个问题这些Key会涨多大失效时间真的分散吗主节点挂了谁来接管三个问题都答得上来Redis这块基本就稳了。