
1. 项目概述一份能让你从入门到精通的Redis实战笔记最近在整理团队的技术资产翻出了自己从2015年到现在积累的Redis笔记从最基础的SET、GET命令到后来支撑千万级日活业务的分布式锁、缓存架构设计再到近两年研究的Redis Stream和RedisJSON。这些笔记散落在各个Markdown文件、云笔记甚至纸质笔记本上每次新人入职或者团队遇到类似问题我都得花时间重新梳理一遍。这次我下定决心把它们彻底整合、更新形成一份结构化的“Redis实战总结笔记”。这份笔记的目标很明确它不是一个简单的命令手册翻译也不是官方文档的复刻。它的核心价值在于串联起“基础命令 - 核心原理 - 生产级项目实战”这条完整的学习与应用路径。无论你是刚接触Redis想用它做个简单的会话缓存还是已经有一定经验需要在复杂分布式系统中解决缓存穿透、雪崩、数据一致性等棘手问题这份笔记都试图提供一个清晰的、经过验证的参考方案。我会把那些在官方文档里一笔带过但在实际项目中却可能让你调试一整晚的“坑”和“技巧”都写进去。2. 核心学习路径与知识体系构建学习任何技术最怕的就是东一榔头西一棒子看似学了很多命令遇到实际问题却无从下手。我根据自己多年的经验将Redis的知识体系划分为三个清晰的阶段每个阶段的目标和产出都不同。2.1 第一阶段夯实基础——理解数据结构与单机操作这个阶段的目标是“会用”。你需要把Redis当成一个超级好用的、支持多种数据结构的“远程字典”来理解。很多初学者卡在这一步是因为一上来就死记硬背几十个命令很快就混乱了。我的建议是以数据结构为纲命令为目。Redis不是只有简单的Key-Value它的Value有5种核心类型每种类型对应不同的应用场景和命令集。String字符串这是最基础的类型。但别小看它除了SET/GET你必须要掌握INCR/DECR计数器、SETNX分布式锁基础、MSET/MGET批量操作以及SETEX/PSETEX带过期时间设置。理解为什么SET key value EX 10 NX这个组合命令是实现简单分布式锁的原子性操作关键。Hash哈希想象成一个Java里的MapString, String。它非常适合存储对象比如用户信息user:1001其字段可以是name,age,city。命令HMSET批量设字段注意新版本建议用HSET、HGETALL获取所有字段小心大Key问题是核心。List列表一个双向链表。它的价值在于“顺序”和“阻塞”特性。LPUSH/RPUSH和LPOP/RPOP可以实现栈或队列。而BLPOP/BRPOP阻塞式弹出是实现简单消息队列的基石。我常用它来做活动期间的通知列表或者任务排队。Set集合无序、唯一元素的集合。SADD、SMEMBERS、SINTER交集、SUNION并集、SDIFF差集是核心。典型场景共同关注的好友交集、给用户打标签一个用户一个Set里面是标签ID。Sorted Set有序集合带权重的Set每个元素关联一个score用于排序。这是Redis里非常强大的一种数据结构。ZADD、ZRANGE按排名取、ZRANGEBYSCORE按分数范围取是基础。排行榜ZREVRANGE取Top N、延迟队列将执行时间作为score是经典应用。注意在学习每个数据结构的命令时一定要同步思考其时间复杂度Time Complexity。比如HGETALL在字段非常多时是O(n)可能成为性能瓶颈ZRANGE在获取大量数据时也一样。养成看命令文档时先看复杂度的习惯。2.2 第二阶段掌握进阶——深入原理与多机扩展当你能够熟练运用各种数据结构解决业务问题后就会自然遇到瓶颈内存不够了怎么办单点故障怎么办并发写冲突怎么办这个阶段的目标是“懂原理会设计”。持久化机制RDB与AOF这是Redis数据安全的基础。你必须理解RDB快照在特定时间点生成整个数据集的二进制压缩文件。优点是文件紧凑恢复速度快。缺点是可能丢失最后一次快照后的所有数据。配置项save 900 1900秒内至少1个key变化则触发需要根据业务容忍度调整。AOF追加日志记录每一个写操作命令。优点是数据安全性高最多丢失1秒数据appendfsync everysec配置下。缺点是文件体积大恢复慢。生产环境通常两者同时开启用RDB做冷备用AOF保证数据安全。实操心得在虚拟机或低配服务器上如果数据量稍大使用save命令或BGSAVE触发RDB持久化时可能会引发Redis进程“卡顿”这是因为fork()子进程在内存较大时耗时较长。监控latest_fork_usec指标可以了解上次fork的耗时。主从复制Replication实现数据备份和读写分离的基础。从节点Slave通过SLAVEOFRedis 5.0前或REPLICAOFRedis 5.0后命令同步主节点Master的数据。要理解全量同步SYNC和增量同步PSYNC的过程。一个重要技巧在从节点配置文件中设置replica-read-only yes确保从节点只读避免配置失误导致数据不一致。高可用与哨兵Sentinel主从复制解决了数据备份但主节点挂了需要手动切换。Sentinel哨兵模式就是解决这个问题的自动化方案。它是一组独立的进程负责监控主从节点并在主节点故障时自动将一个从节点提升为新主并让其他从节点指向新主。部署要点哨兵必须是奇数个如3个以避免脑裂时的选举僵局。哨兵之间以及哨兵与所有Redis节点都需要网络互通。客户端连接你的应用程序不再直接连接Redis主节点而是连接哨兵集群由哨兵告诉客户端当前可用的主节点地址。2.3 第三阶段项目实战——应对复杂场景与性能调优这是将知识转化为生产力的阶段。你会面对真实的、复杂的业务场景需要组合运用多种知识来设计解决方案。缓存设计模式与问题缓存穿透查询一个根本不存在的数据请求会穿透缓存直达数据库。解决方案1. 对不存在的Key也缓存一个空值如NULL并设置较短的过期时间。2. 使用布隆过滤器Bloom Filter在查询缓存前进行预检。缓存击穿某个热点Key过期瞬间大量请求同时涌入数据库。解决方案1. 设置热点Key永不过期通过后台任务异步更新。2. 使用互斥锁如Redis分布式锁只允许一个线程去数据库加载数据其他线程等待。缓存雪崩大量Key在同一时间点或时间段过期导致所有请求涌向数据库。解决方案1. 给缓存过期时间加上一个随机值如基础30分钟随机0-5分钟打散过期时间。2. 保证Redis服务的高可用哨兵/集群避免Redis本身宕机。分布式锁这是面试高频题也是实战难点。基于SETNX的简单锁问题很多如忘记释放、客户端崩溃。更健壮的做法是使用Redlock算法有争议但需了解或者直接使用成熟的客户端库如Redisson它实现了可重入锁、读写锁、公平锁等并内置了看门狗Watchdog机制自动续期比自己实现要可靠得多。集群Cluster模式当数据量或吞吐量超过单机极限时必须使用集群。Redis Cluster采用无中心节点的P2P架构数据按哈希槽slot共16384个分片存储在不同的主节点上。键路由客户端会缓存一份槽位映射表通过计算Key的CRC16值然后对16384取模决定将请求发往哪个节点。迁移与扩容集群支持在线重新分片可以将某个槽位的键值从节点A迁移到节点B。这是运维中的高级操作需要谨慎执行。3. 核心细节解析与生产环境实操要点知道理论是一回事能稳定地用在生产环境是另一回事。下面我分享几个在实战中总结出的、关乎稳定性和性能的核心细节。3.1 内存优化与Big Key治理Redis是内存数据库内存就是命脉。不合理的Key设计是内存暴涨和性能劣化的首要元凶。什么是Big Key通常指一个Key对应的Value体积过大如超过10KB的String或元素数量超过5000的Hash/List/Set/ZSet。它会带来操作耗时长阻塞Redis单线程模型。网络传输压力大。集群模式下数据迁移困难。删除时可能引发长时间阻塞DEL大Key。如何发现Big Key使用官方工具redis-cli --bigkeys命令可以快速扫描但这是一个线上扫描命令会对Redis产生压力请在业务低峰期执行。使用内存分析工具对于RDB文件可以使用redis-rdb-tools进行分析生成内存报告精确看到每个Key的大小。监控告警通过INFO memory命令监控used_memory趋势并设置告警阈值。如何治理Big Key拆分将一个大的Hash拆分成多个小的Hash。例如user:1001:info存储了用户所有信息可以拆成user:1001:base基础信息、user:1001:contact联系信息等。使用HSCAN命令分批次读取。压缩对于存储JSON或文本的String可以考虑在客户端进行压缩如GZIP后再存入Redis读取时再解压。这需要权衡CPU和网络开销。使用合适的数据结构存储用户粉丝列表用Set可能元素过多考虑是否可以用布隆过滤器来近似判断“是否关注”。设置过期时间给所有缓存Key都设置合理的TTL避免无用数据常驻内存。3.2 持久化配置与数据安全权衡生产环境的持久化配置直接决定了数据的安全等级和性能表现。没有“最好”的配置只有“最适合”的配置。混合持久化RDBAOF的推荐配置# redis.conf save 900 1 # 15分钟内至少1个key变化则触发RDB快照。根据写频率调整如果写很少可以调大或关闭。 save 300 10 # 5分钟内至少10个key变化 save 60 10000 # 1分钟内至少10000个key变化 appendonly yes # 开启AOF appendfilename appendonly.aof # AOF文件名 appendfsync everysec # 每秒同步一次在安全与性能间取得平衡 no-appendfsync-on-rewrite no # AOF重写时不阻塞主进程的fsync操作 auto-aof-rewrite-percentage 100 # AOF文件比上次重写后体积增长100%时触发重写 auto-aof-rewrite-min-size 64mb # AOF文件体积至少64MB时才触发重写 aof-use-rdb-preamble yes # 开启混合持久化Redis 4.0重写后的AOF文件包含RDB格式的全量数据和增量AOF日志恢复更快。这个配置提供了多层保障定时RDB快照提供历史备份和快速恢复能力每秒同步的AOF保证最多丢失1秒数据混合持久化让AOF重写和恢复效率更高。备份策略本地备份利用crontab定时任务在业务最低谷时如凌晨4点执行SAVE或BGSAVE命令SAVE会阻塞BGSAVE后台进行然后将生成的RDB文件拷贝到其他服务器或对象存储。从节点备份在一个专用的从节点上配置不同的、更激进的持久化策略比如save 关闭RDB只开AOF且appendfsync always追求极致安全专门用于备份即使备份影响性能也不会波及主节点服务。重要提示appendfsync always虽然能保证每个写命令都落盘数据最安全但会严重拖慢Redis的写入性能除非对数据一致性有极端要求如金融交易否则生产环境不推荐使用。3.3 高可用架构哨兵模式部署详解哨兵模式是中小规模项目实现Redis高可用的标配。下面是一个3节点1主2从 3哨兵的最小高可用集群部署实操。节点规划Redis Master: 192.168.1.10:6379Redis Slave1: 192.168.1.11:6379 (复制Master)Redis Slave2: 192.168.1.12:6379 (复制Master)Sentinel1: 192.168.1.10:26379 (监控集群)Sentinel2: 192.168.1.11:26379Sentinel3: 192.168.1.12:26379Redis主从配置 在Master10的redis.conf中常规配置即可。在两个Slave节点11, 12的redis.conf中关键配置如下# slave1和slave2的配置 replicaof 192.168.1.10 6379 # 指定主节点Redis 5.0后用这个之前用slaveof masterauth your_strong_password # 如果主节点有密码需要配置 replica-read-only yes # 从节点只读非常重要哨兵配置 三个哨兵节点的sentinel.conf配置几乎相同以Sentinel1为例port 26379 daemonize yes pidfile /var/run/redis-sentinel.pid logfile /var/log/redis/sentinel.log # 核心配置监控名为mymaster的主节点地址为192.168.1.10:6379 # 2表示至少需要2个哨兵同意才判定主节点客观下线并触发故障转移 sentinel monitor mymaster 192.168.1.10 6379 2 # 主节点密码如果设置了 sentinel auth-pass mymaster your_strong_password # 判定主节点主观下线的时间毫秒超过这个时间哨兵认为主节点“失联” sentinel down-after-milliseconds mymaster 5000 # 故障转移时从节点同步新主数据的超时时间 sentinel parallel-syncs mymaster 1 # 故障转移超时时间 sentinel failover-timeout mymaster 180000配置好后分别启动三个Redis实例和三个哨兵实例。客户端连接 你的应用程序如Java使用Jedis或Lettuce不再直连192.168.1.10:6379而是连接哨兵集群。以Spring Boot配置为例spring: redis: sentinel: master: mymaster # 与sentinel.conf中监控的名称一致 nodes: 192.168.1.10:26379,192.168.1.11:26379,192.168.1.12:26379 password: your_strong_password客户端库会通过哨兵自动发现当前可用的主节点地址。当主节点切换后哨兵会通知客户端更新连接。4. 项目实战构建一个抗高并发的商品详情页缓存系统让我们用一个电商场景来串联前面所有的知识点。假设我们要为一个日均PV数亿的商品详情页设计缓存架构核心诉求是极高读取性能、保证数据最终一致性、防止缓存失效引发的数据库雪崩。4.1 架构设计与技术选型多级缓存架构L1本地缓存Caffeine/Guava Cache在应用服务器内存中缓存极热数据如Top 100商品。命中时零网络开销速度最快。设置较小的容量和较短的TTL如5秒主要用于应对瞬时热点。L2分布式缓存Redis Cluster缓存全量商品数据。作为主力缓存层承担绝大部分读请求。我们选择Redis Cluster而非哨兵模式因为商品数据量巨大亿级且需要更高的吞吐量Cluster模式可以通过分片横向扩展。后端存储MySQL商品基础信息 MongoDB商品详情、评价等大文本。缓存策略采用经典的Cache-Aside Pattern旁路缓存。这是最常用、最可控的模式。读流程先读L1 - 未命中则读L2 - 仍未命中则读DB - 将数据写入L2和L1。写流程更新DB -删除L2和L1中的缓存数据。注意是删除DEL而非更新这是为了简单和避免并发写导致的数据不一致。下次读请求自然会触发缓存重建。4.2 核心环节实现与代码示意我们重点看L2缓存Redis的读写逻辑实现以及如何解决经典问题。缓存键Key设计// 商品基础信息缓存键 private static final String CACHE_KEY_PRODUCT_BASE “product:base:%s”; // %s 为商品ID // 商品库存缓存键库存更新频繁单独缓存 private static final String CACHE_KEY_PRODUCT_STOCK “product:stock:%s”;使用冒号分隔的命名空间清晰且易于通过KEYS product:base:*模式匹配进行管理生产环境慎用KEYS推荐用SCAN。防缓存击穿实现 使用Redis分布式锁确保只有一个线程去数据库加载数据。public Product getProductById(Long id) { String cacheKey String.format(CACHE_KEY_PRODUCT_BASE, id); // 1. 先查缓存 Product product redisTemplate.opsForValue().get(cacheKey); if (product ! null) { return product; } // 2. 缓存未命中尝试获取分布式锁 String lockKey “lock:product:” id; String lockValue UUID.randomUUID().toString(); // 锁的值用于安全释放 try { // 使用SET命令的NX和PX参数实现原子性加锁 Boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, lockValue, Duration.ofSeconds(10)); // 锁持有10秒 if (Boolean.TRUE.equals(locked)) { // 3. 获取锁成功再次检查缓存Double Check product redisTemplate.opsForValue().get(cacheKey); if (product ! null) { return product; } // 4. 查询数据库 product productMapper.selectById(id); if (product ! null) { // 5. 写入缓存设置随机过期时间防雪崩 int expireTime 1800 new Random().nextInt(300); // 1800-2100秒随机 redisTemplate.opsForValue().set(cacheKey, product, expireTime, TimeUnit.SECONDS); } else { // 6. 数据库也没有缓存空值防穿透 redisTemplate.opsForValue().set(cacheKey, NullValue.INSTANCE, 300, TimeUnit.SECONDS); } return product; } else { // 7. 获取锁失败说明有其他线程在加载短暂休眠后重试或返回降级数据 Thread.sleep(50); return getProductById(id); // 简单重试或返回一个默认商品/错误信息 } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(“获取商品信息中断”, e); } finally { // 8. 释放锁使用Lua脚本保证原子性避免误删其他线程的锁 String luaScript “if redis.call(‘get’, KEYS[1]) ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end”; redisTemplate.execute(new DefaultRedisScript(luaScript, Long.class), Collections.singletonList(lockKey), lockValue); } }这段代码包含了几个关键点缓存空值防穿透、分布式锁防击穿、双重检查锁DCL、锁的原子性释放Lua脚本、随机过期时间防雪崩。数据一致性保障最终一致性 写操作时先更新数据库再删除缓存。但“删除缓存”可能失败。我们引入一个“重试机制”。方案一消息队列更新DB后发送一条删除缓存的消息到MQ如RocketMQ/Kafka。消费者监听队列执行删除。如果失败消息会被重投直到成功。方案二订阅数据库Binlog使用Canal或Debezium等工具订阅MySQL的Binlog。当监听到商品表更新时解析日志发出删除缓存的事件。这个方案对业务代码无侵入更优雅。4.3 集群模式下的注意事项当使用Redis Cluster时上述代码需要微调批量操作MSET、MGET等批量命令要求所有Key必须在同一个哈希槽。对于product:base:1001和product:base:1002这种不同的Key它们可能分布在不同的节点上无法使用MGET。解决方案是使用Hash Tag即用{}将Key中保证在同一槽位的部分括起来例如{product}:base:1001和{product}:base:1002这样只会对product部分计算槽位它们就能在同一节点了。但需谨慎使用避免导致数据倾斜。事务与Lua脚本在Cluster模式下事务和Lua脚本中的所有Key也必须位于同一个节点。在编写上述释放锁的Lua脚本时锁的KeylockKey必须与它要操作的缓存KeycacheKey在同一个节点否则脚本执行会报错。这通常意味着锁的Key设计需要与业务Key相关联。5. 常见问题与排查技巧实录即使设计再完善线上问题依然难免。下面是我在运维Redis集群时遇到的一些典型问题及排查思路。5.1 性能问题排查现象客户端请求延迟P99明显增高Redis服务器CPU或内存使用率异常。排查步骤慢查询分析使用SLOWLOG GET 10命令获取最近10条慢查询日志。慢查询阈值可以通过slowlog-log-slower-than配置单位微秒默认10000即10毫秒。分析是哪些命令、哪些Key慢了。常见原因KEYS *、HGETALL一个大Hash、LRANGE一个很长的List。监控命令统计使用INFO commandstats命令查看所有命令的调用次数、总耗时、平均耗时。可以帮你发现哪个命令是“性能杀手”。检查持久化如果latest_fork_usec值很高如超过1秒说明RDB或AOF重写时fork操作阻塞了主进程。考虑在从节点做持久化或者升级机器配置fork耗时与内存量正相关。检查内存和网络INFO memory查看used_memory、mem_fragmentation_ratio内存碎片率大于1.5可能需要关注。使用redis-cli --latency和redis-cli --latency-history测试网络延迟。检查客户端连接INFO clients查看连接数。如果连接数异常多可能是连接池配置不当或客户端有连接泄漏。CLIENT LIST可以查看具体连接的详情。5.2 内存问题排查现象used_memory持续增长接近maxmemory限制或触发逐出Eviction。排查步骤确认逐出策略CONFIG GET maxmemory-policy。生产环境常用allkeys-lru或volatile-lru。确保策略符合预期。分析内存使用使用redis-rdb-tools这是最强大的工具。导出RDB文件运行rdb -c memory dump.rdb --bytes 128 --largest 20可以生成CSV报告列出内存占用最大的20个Key及其类型、大小、元素数量等。使用MEMORY USAGE命令对可疑的Key执行MEMORY USAGE key估算其内存消耗。查找无过期时间的Key通过脚本扫描所有Key使用SCAN而非KEYS用TTL key命令检查将TTL为-1永不过期的Key记录下来评估其必要性。检查复制缓冲区如果主从节点网络延迟大主节点的复制输出缓冲区client-output-buffer-limit replica可能积压导致内存飙升。监控INFO replication中的slave_repl_offset和master_repl_offset差值。5.3 主从复制与哨兵问题现象从节点数据延迟大或哨兵故障转移失败。排查步骤复制延迟INFO replication查看主从节点的master_repl_offset和slave_repl_offset两者的差值就是延迟的字节数。延迟大的常见原因网络带宽不足、从节点性能差持久化或读请求压力大、主节点写入流量过高。解决方案优化网络、提升从节点配置、在从节点上关闭持久化如果可接受数据丢失风险、使用多个从节点分摊读压力。哨兵脑裂Split-Brain极端网络分区下可能出现两个主节点。预防至少部署3个奇数个哨兵实例并分布在不同的物理机器或可用区。合理配置quorum法定人数和down-after-milliseconds参数。排查查看各哨兵的日志sentinel.log分析其观点。SENTINEL master mymaster命令可以查看哨兵认为的主节点是谁。故障转移失败检查哨兵之间以及哨兵与Redis节点之间的网络连通性。检查masterauth和sentinel auth-pass配置的密码是否正确。检查从节点的replica-read-only配置确保为yes否则它可能无法被提升为主节点。5.4 连接与客户端问题现象客户端报连接超时、连接池耗尽等错误。排查步骤连接数超限CONFIG GET maxclients查看最大连接数限制。使用INFO clients查看当前连接数。如果接近限制检查客户端连接池配置是否过大或是否存在连接未关闭的情况。客户端超时检查客户端配置连接超时、读写超时时间是否设置过短。在网络不稳定或Redis负载高时适当调大。检查Redis负载如果Redis CPU持续100%命令执行慢会导致客户端读写超时。按“性能问题排查”步骤处理。检查慢查询一个慢查询会阻塞整个Redis导致其他客户端请求排队超时。连接池配置这是客户端侧最常见的问题。以Java的Lettuce/Jedis为例需要合理配置最大连接数、最小空闲连接数、最大等待时间等。配置过小会导致高并发下等待配置过大会浪费资源并给Redis带来压力。需要根据实际业务压测来定。一个真实的踩坑案例我们曾遇到一个服务在每晚定时任务期间大量报Redis超时。排查后发现该任务会用一个循环HGETALL读取上千个大的Hash Key。每个HGETALL都可能耗时几十到上百毫秒由于Redis单线程处理这些命令排队执行总耗时达到数分钟直接拖垮了整个Redis实例导致其他服务的所有请求超时。解决方案将大Hash拆分成小Hash并使用HSCAN进行流式分批读取或者将这类离线批处理任务迁移到专门的、不影响在线业务的从节点上去执行。