Redis 这个组件的知识点有个特点单看每一条都不难难的是它们互相咬合。持久化和主从有关系主从和哨兵、Cluster 有关系内存淘汰策略又直接决定缓存雪崩的应对姿势而事务、Lua、分布式锁这三块本质上是在回答同一个问题——怎么让多条命令看起来像一次操作。我自己前几年学 Redis 完全是打补丁式的遇到大 key 查大 key遇到雪崩查雪崩脑子里是一堆散落的卡片。真正把它串起来是从动手画一张 Redis 思维导图开始的。下面这张导图的骨架是我自己用了挺久、也改过好几版的一版不是把官方文档抄一遍而是按看到一个问题能顺着分支找到答案的标准来组织的。这篇内容适合两类人一类是刚把 Redis 当缓存用起来、命令会敲但说不清底层编码和淘汰策略的另一类是准备做系统性梳理、想把零散知识收成一张图的老手。我会把每个分支该挂哪些节点、哪些节点是必须展开的、哪些是画上去占地方但没用的都讲清楚包括配置项的默认值和几个容易记混的阈值。图你自己画但骨架和填充物可以直接拿去用。1. 先定骨架第一层分叉切错了后面全是白画画 Redis 思维导图第一层怎么切决定了整张图的命运。我见过太多人第一层直接写String、List、Hash、Set、ZSet然后就没了图长成一张命令手册。也见过第一层按缓存、分布式锁、消息队列切结果持久化、主从这些内容无处安放。这两种切法的问题都在于它们是按我平时怎么用来切的而不是按这个系统由哪些部分组成来切的。1.1 三种切法的取舍我前后试过三种第一层切法各自的毛病很明确。按数据类型切好处是上手快、和文档目录一致坏处是所有非数据结构的主题被迫挤进一个其他分支主从、哨兵、集群、慢查询全塞进去那条分支会长得没法看。按使用场景切像缓存场景计数场景队列场景看着实用但它把同一套底层机制拆散在多个分支里比如缓存雪崩和内存淘汰策略本该相邻场景切法会把它们隔开老远。按问题域切也就是我要解决什么问题比如数据放哪、怎么存挂了怎么办慢了怎么办这个思路更贴近排查现场。我最后采用的是四象限数据结构、可靠性、性能与内存、运维与观测外围再挂一条横切分支并发与原子性。理由是这四块各自独立、边界清楚而并发与原子性是一条贯穿线——事务、Lua、分布式锁、Pipeline 都在这条线上它们会影响其他四个象限但不属于其中任何一个。1.2 四象限 一条横切线的具体布局落到图上就是五个主分支。数据结构分支回答能存什么、底层怎么存可靠性分支回答进程挂了、机器挂了、机房挂了分别怎么办对应持久化、主从、哨兵、Cluster 四个层级性能与内存分支回答内存满了怎么办、过期怎么删、大 key 怎么找运维观测分支回答出事了看什么指标、哪些命令不能在生产敲。横切线是并发与原子性挂事务、Lua、分布式锁、Pipeline。这样切的额外好处是每个分支都能标一个故障层级的坐标。持久化解决的是单进程重启主从解决的是单机硬件故障哨兵和 Cluster 解决的是节点级故障再往上才是跨可用区的问题。画图的时候我会在可靠性分支旁边竖着写一行小字标注这几个层级复习的时候一眼就能看出我现在关心的是哪一层的问题不至于把哨兵和持久化混为一谈——这两个是新手最容易搞混的一对。2. 数据结构分支五个基础类型只是入口编码才是重点五个基础类型String、List、Hash、Set、ZSet在导图上应该只占最小的一块因为它们只是对外暴露的接口。真正值得展开的是同一个类型在不同数据规模下会切换到不同的底层编码这件事直接决定了内存占用和某些命令的复杂度也是面试里区分用过和懂的分水岭。2.1 编码节点怎么挂我的做法是每个类型下面挂三个子节点命令簇、底层编码、编码转换阈值。命令簇不要全列只挂高频的和易错的比如 ZSet 下面只挂ZADD、ZRANGEBYSCORE、ZRANGE注意 6.2 之后 ZRANGE 支持 BYSCORE/BYLEX/REV 参数老版本的ZREVRANGEBYSCORE已经可以不用了。底层编码和转换阈值用一张表贴在导图节点旁边比文字描述好记。类型编码一编码二编码三关键阈值配置Stringintembstrraw44 字节分界embstr 上限Listlistpackquicklist-list-max-listpack-size默认 128Hashlistpackhashtable-hash-max-listpack-entries128 /hash-max-listpack-value64Setintsetlistpackhashtableset-max-intset-entries512ZSetlistpackskiplist-zset-max-listpack-entries128 /zset-max-listpack-value64这张表里有个细节值得单独在导图上圈出来String 的 embstr 上限是 44 字节不是 39 也不是 45因为 Redis 的对象头加上 SDS 头的固定开销正好把这些字节吃掉剩下的才归你存数据。这个数字很多人背过但没想过为什么我在导图上就在 embstr 节点旁边标注了44 64 - 头部开销。2.2 为什么编码这件事值得单独开一个子分支因为编码决定了内存和复杂度。举个具体的一个 Hash 存 100 个字段如果每个值都不超过 64 字节它会是 listpack 编码此时HGET是线性扫描但因为元素少、都在连续内存里实际比哈希表还快而且省掉了大量指针开销。一旦字段数超过 128 或者某个值超过 64 字节它会整体转成 hashtable内存占用可能翻好几倍而且转换是单向的——删掉字段变回 100 个以下编码也不会退回 listpack。这个单向性在实际项目里坑过我一次某业务把配置项塞进一个 Hash高峰期临时加了一批字段超过 128编码转成 hashtable之后字段数降回来内存也没释放。当时排查了半天以为是内存泄漏最后OBJECT ENCODING一看就明白了。所以我在导图上给每个类型的编码节点都加了一个反向箭头的叉号标注不可逆。平时用OBJECT ENCODING key就能直接看当前编码这个命令应该挂在导图的数据结构分支顶部作为入口工具。2.3 Bitmap、HyperLogLog、GEO、Stream 这条扩展支线扩展类型单独拉一条支线不要塞进五个基础类型里。Bitmap 挂在 String 下面更合理因为它本质就是 String 的位操作SETBIT、BITCOUNT、BITOP这组命令在签到、活跃统计场景里非常省内存——一亿用户的签到状态大约 12MB这个数量级对比要在导图上标出来因为它是选型时最有说服力的数字。HyperLogLog 要标注的是它的性质估算、有误差、标准误差约 0.81%、固定 12KB 内存、只支持计数不支持取回元素。我见过有人拿它存 UV 之后想查某用户今天来过没有那是不可能的这个节点必须写清楚不可逆。GEO 底层是 ZSet 的 skiplist用 geohash 编码做分值所以 GEO 分支和 ZSet 分支之间应该画一条虚线连接这个关联比单独记 GEO 命令有用。Stream 是唯一一个能当消息队列用的类型挂XADD、XREADGROUP、XACK、XPENDING旁边标注消费者组和 pending 列表的概念以及它和 Pub/Sub 的本质区别Pub/Sub 不持久化、断线即丢Stream 会留。3. 可靠性分支持久化、主从、哨兵、集群的四层防御这是整张图里信息密度最高、也最容易被画成流水账的一块。我的组织方式是按故障层级纵向排列而不是按组件横向平铺。纵轴从上到下依次是进程重启持久化、单机不可用主从 哨兵、容量或写入瓶颈Cluster。这样排的好处是复习时能顺着假设现在挂了一层下一层怎么兜底往下走。3.1 RDB 与 AOF 的节点设计RDB 分支下面挂三个节点触发方式、fork 与写时复制、优缺点。触发方式分SAVE阻塞生产禁用、BGSAVE、以及配置里的save 900 1 / save 300 10 / save 60 10000三档。fork 与写时复制这块必须在导图上写明白BGSAVEfork 出子进程子进程共享父进程的内存页父进程写入时触发 COW 复制页所以内存占用最坏情况可能翻倍这也是为什么单机内存不要跑到 60% 以上。这个结论比背RDB 快有用得多。AOF 分支挂四件事刷盘策略、重写机制、重写缓冲区、混合持久化。刷盘策略是appendfsync的三个值always每条都刷最安全最慢、everysec每秒刷默认最多丢 1 秒、no交给操作系统通常 30 秒左右。我建议在导图上把这三个值对应的最多丢多少数据直接写在旁边因为这是选型时唯一真正要权衡的东西。重写机制里有个坑值得单独画一个节点AOF 重写期间主进程还在写这些新命令会同时写入AOF 重写缓冲区重写完成后追加到新文件末尾。如果不理解这个缓冲区就解释不了为什么重写期间内存会涨。混合持久化aof-use-rdb-preamble yes4.0 之后默认开是把重写后的文件前半段做成 RDB 格式、后半段是增量 AOF重启时先加载 RDB 再回放增量速度比纯 AOF 快一个数量级。这个配置我建议默认就开着导图上把它标成绿色的推荐开启。3.2 主从复制的三个阶段主从分支别只写全量同步 增量同步要拆成三个明确的阶段建立连接与握手、全量同步、命令传播。握手阶段核心是PSYNC命令带上runid和offset从节点第一次连上来不知道自己该从哪同步会发PSYNC ? -1主节点回FULLRESYNC runid offset然后开始传 RDB。这里有两个关键结构必须挂在导图上repl_backlog_buffer复制积压缓冲区环形默认 1MB由repl-backlog-size控制和master_replid。增量同步能不能成功取决于从节点断线期间产生的写命令有没有被环形缓冲区覆盖。缓冲区太小断线几秒就溢出只能走全量全量又要 fork 又要传文件一来回就是几百毫秒到几秒的抖动。所以我在导图上给repl-backlog-size标了按写入量估算别用默认值。命令传播阶段就是主节点把写命令异步发给从节点这里要标注异步两个字因为它直接推出一个结论Redis 主从不能保证强一致主节点写完返回成功时从节点可能还没收到。这个结论往上层连接着哨兵故障转移时的数据丢失风险往下连着一堆业务的坑是导图上必须画箭头的一个节点。3.3 哨兵和 Cluster 的分工以及两条容易混的线哨兵分支挂四件事主观下线sdown单个哨兵认为挂了、客观下线odown超过quorum数量的哨兵都认为挂了、Leader 选举、故障转移。这里最容易记混的是quorum和majority的区别quorum只用来判断客观下线真正选举 Leader 需要过半票数两个数字可以不一样。我见过配置里quorum设成 2 但部署了 5 个哨兵的情况判断下线很快但选举要 3 票理解了这个区分才不会配错。Cluster 分支挂四件事16384 个槽、槽分配与迁移、MOVED与ASK重定向的区别、cluster-require-full-coverage。MOVED是槽已经永久迁走了客户端应该更新本地槽表ASK是槽正在迁移中只是临时去目标节点问一下客户端不能更新槽表。这两个的区别几乎每次讲集群都要强调一遍因为客户端实现错了会导致槽表抖动。cluster-require-full-coverage默认是yes意味着只要有一个槽没人负责整个集群就拒绝服务很多人不知道这个默认值节点一挂就全站不可用还以为是集群坏了。我在导图上把哨兵和 Cluster 之间画了一条明确的二选一分界线因为这两个不是叠加关系而是替代关系。主从 哨兵解决的是高可用Cluster 解决的是分片 高可用但 Cluster 的运维复杂度高一个量级槽迁移、MOVED处理、跨槽操作限制都是成本。数据量不大、单机内存能装下的场景主从加哨兵更省心。4. 内存与过期分支一句话能写完实际问题全在这这个分支是我见过被画得最虚的很多人就写一行定期删除 惰性删除和一行八种淘汰策略就完事。但实际上线上 Redis 出问题一半以上出在这里。我的做法是把它拆成三个子节点过期删除、内存淘汰、内存碎片与大 key。4.1 过期删除的两条腿惰性删除是指访问 key 的时候才检查是否过期过期了就删。这条腿的问题是如果某个 key 过期了但一直没人访问它就永远占着内存。定期删除是补这个漏的Redis 默认每 100 毫秒执行一次随机抽 20 个设置了过期时间的 key删掉其中过期的如果过期比例超过 25%就再抽 20 个继续直到比例降下来或者到达时间上限默认不超过 25% CPU 时间。这个25% 比例触发继续抽样的机制我建议在导图上明确写出来因为它解释了一个常见现象一批 key 在同一时刻集中过期时定期删除会疯狂抽样CPU 出现毛刺。这就是为什么大规模缓存写入时要在过期时间上加随机抖动比如基础 TTL 加 0 到 300 秒的随机值。这个技巧很多人知道但不知道为什么画图时把因果关系连上就记住了。4.2 八种淘汰策略的选择逻辑淘汰策略不要平铺八个名字要按两个维度做矩阵淘汰范围allkeys全部键 /volatile只淘汰设置了过期时间的键和淘汰算法lru、lfu、random、ttl再加一个noeviction。这样就是 2×419 种实际上allkeys-ttl是不存在的所以是 8 种。策略范围行为适用场景noeviction-内存满时写入报错数据不可丢、当数据库用allkeys-lru全部淘汰最近最少使用通用缓存最常用allkeys-lfu全部淘汰访问频率最低有明显热点、抗扫描allkeys-random全部随机淘汰几乎不用volatile-lru有过期时间淘汰最近最少使用混合存储部分数据要保留volatile-lfu有过期时间淘汰频率最低同上热点明显时volatile-random有过期时间随机淘汰几乎不用volatile-ttl有过期时间淘汰更早过期的短期缓存为主这里有个必须讲清楚的点Redis 的 LRU 是近似 LRU不是精确的。它每个 key 里存了一个 24 位的时间戳淘汰时随机采样maxmemory-samples个 key默认 5从里面挑最久没用的。采样数越大越接近真实 LRU但 CPU 开销也越大。LFU 是 4.0 引入的用 8 位计数器加 8 位时间衰减能抗住扫一遍全量 key 就把 LRU 污染了的问题。如果你的业务存在批量扫描或者定时任务全量读的场景allkeys-lru很容易把真正的热点淘汰掉这时候换成allkeys-lfu效果会明显不一样。maxmemory这个配置本身也有讲究不能设成实例的物理内存大小因为还有 fork 时的 COW 复制、主从复制缓冲区、客户端输出缓冲区这些额外开销。我的经验值是设成物理内存的 60% 到 70%剩下的留给这些。这个数字没有什么理论公式是按最坏情况估的BGSAVE期间如果写入很猛内存翻倍是可能的。4.3 大 key 和内存碎片大 key 单独挂一个节点因为它是独立于淘汰策略的一类问题。判定标准不统一我按String 超过 10KB、集合类元素超过 5000 个来划。危害有三个删除时阻塞DEL大 key 是同步的几百万个元素的 Hash 删除可能卡住几百毫秒、网络阻塞一次HGETALL拉几十 MB、集群下数据倾斜。处理方法是在导图上写清楚用UNLINK代替DEL异步删除用HSCAN/SSCAN/ZSCAN分批遍历写入时就从设计上拆 key。内存碎片看mem_fragmentation_ratioused_memory_rss / used_memory1 到 1.5 之间正常大于 1.5 说明碎片多可以用activedefrag yes开启主动碎片整理但这个功能会吃 CPU要在低峰期开。小于 1 说明发生了 swap这是更严重的问题性能会断崖式下降。这几个指标和阈值我直接写在导图上配合监控一起看。5. 缓存问题分支把因果箭头画出来才有用穿透、击穿、雪崩这三个词很多人能背定义但分不清原因是它们被当成三个并列的名词记。我在导图上把它们摆成一个因果链每个下面挂现象—根因—方案方案之间再画交叉箭头因为实际方案经常是复用的。5.1 三者的本质区别问题根因现象主要方案穿透查不存在的数据请求全打到存储层布隆过滤器、空值缓存击穿单个热点 key 过期瞬时并发全打到存储层互斥重建、逻辑过期雪崩大量 key 同时过期或实例挂大面积请求打到存储层过期抖动、多级缓存、熔断降级穿透的核心是数据本来就不存在所以缓存永远不命中。方案里布隆过滤器是在缓存之前加一层把所有可能存在的 key 的哈希位预先置好查询时先过一遍判定不存在就直接返回。要注意的是布隆过滤器有假阳性说不存在一定不存在说存在可能不存在而且不支持删除商品下架这种场景要么用计数型布隆过滤器要么定期重建。空值缓存是简单粗暴的兜底把查不到的 key 也缓存一个空值TTL 设短一点比如 60 秒。这两种方案在导图上要标注可以叠加使用。5.2 击穿的两种解法差别很大互斥重建的思路是只让一个请求去查数据库其他请求等着。实现上用SET lock_key value NX PX 10000抢锁抢到的去查库回填缓存没抢到的短暂休眠后重试。这个方案保证一致性但牺牲了可用性——高峰期大量请求在等锁接口响应时间会拉长。逻辑过期的思路是不给缓存设真实 TTL而是在 value 里塞一个逻辑过期时间读的时候发现逻辑过期了就异步起一个线程去重建当前请求先返回旧数据。这个方案保证可用性但返回的是旧数据。两种方案的选择取决于业务能不能接受短暂脏数据。我在导图上把这两个方案画成一条分叉标注一致性优先/可用性优先比单纯列方案有用。5.3 双写一致性的几种姿势这块挂三个节点Cache Aside先更新库再删缓存、延迟双删、订阅变更日志。Cache Aside 是主流做法删缓存而不是更新缓存因为更新缓存有两个并发写请求时可能产生乱序覆盖。但先更新库再删缓存仍然有极小的不一致窗口读请求在删缓存之前读到了旧值并回填删缓存就白删了。延迟双删是在更新库之后删一次睡几百毫秒再删一次把窗口期压小。这个方案的问题在于睡眠时间难定而且第二次删除放在业务线程里会拖慢接口一般用异步线程或者消息队列做。订阅变更日志比如数据库的 binlog 变更流是最终一致的做法把删缓存的动作解耦出去可靠性最高但引入了额外组件。导图上我会把这三者按一致性强度和实现复杂度两个维度摆开方便选型时对照。6. 并发与原子性一条横切线串起四块内容这条分支不放在四个象限里因为它横跨。事务、Lua、分布式锁、Pipeline 这四块看起来是四个东西实际上都在回答同一个问题怎么让多条操作在并发下表现得像一个整体。把它们放在一起对比比分散记要清晰得多。6.1 Redis 事务跟数据库事务不是一回事MULTI开启、命令入队、EXEC执行中间可以DISCARD放弃还能用WATCH做乐观锁。关键点是Redis 事务没有回滚。如果入队阶段有语法错误EXEC时整个事务不执行但如果语法没错、运行时出错比如对 String 执行LPUSH其他命令照样执行成功出错的命令返回错误而已。这个特性必须挂在导图上因为它是选型的核心依据。没有回滚意味着它只能保证命令连续执行不被其他客户端插入不能保证要么全成功要么全失败。另外WATCH是乐观锁EXEC时如果被 watch 的 key 变了整个事务返回 nil需要业务层重试。这些约束让 Redis 事务的适用范围很窄实际项目里用得并不多。6.2 Lua 脚本才是真正的原子执行Lua 脚本在本子上是单线程串行执行的执行期间不会插入其他客户端的命令所以它天然具备原子性而且能写条件逻辑——这是事务做不到的。先判断库存够不够够就扣减这种逻辑必须用 Lua用事务写不了因为事务里拿不到前一条命令的结果。实际使用要用EVAL加载脚本体、或者SCRIPT LOAD拿到 sha1 后用EVALSHA执行前者每次都传脚本内容浪费带宽。这里有个坑要写进导图EVALSHA如果遇到脚本没缓存会报NOSCRIPT客户端要捕获这个错误、回退到EVAL重新加载。很多客户端库已经封装了这个逻辑但自己写的时候必须处理。还有一个约束容易忽略Lua 脚本执行时间过长会阻塞整个实例默认lua-time-limit是 5 秒超时后 Redis 不会自动杀掉脚本只会开始对其他请求返回 BUSY 错误。所以脚本里绝对不能写循环遍历大量 key 或者复杂计算。6.3 分布式锁SET NX PX 之后还有三件事基础的加锁命令是SET lock_key unique_value NX PX 30000NX保证互斥PX防止死锁unique_value用来标识持有者。解锁必须用 Lua 脚本先比对 value 再删不能直接DEL否则可能删掉别人的锁。这两步是标配但导图上还要挂三件后续的事。第一是锁续期。业务执行时间超过锁的过期时间锁会自动释放另一个线程就能进来这就有并发风险。解决办法是 watchdog 机制后台线程定期检查业务是否还在跑还在跑就延长锁的过期时间。第二是可重入同一个线程重复加锁要能成功需要用 Hash 结构存锁名 → 持有者 → 重入次数。第三是集群下的锁有效性主从异步复制意味着主节点加锁成功后还没同步就挂了从节点升主后锁就丢了这是 Redlock 想解决的问题——向多数节点加锁超过半数成功才算成功。但 Redlock 本身也有争议依赖时钟假设实际项目里更常见的做法是接受这个小概率风险或者在锁之上再加一层幂等校验。我在导图上给这个分支写了一句总结性的话分布式锁解决的是减少重复执行不是绝对只执行一次最终一致性要靠业务侧的幂等来兜。这句话比任何方案对比都重要。6.4 Pipeline 解决的是网络往返不是原子性Pipeline把多条命令一次性发出去减少 RTT性能提升在跨机房场景下非常明显。但很多人在导图上把它和事务画在一起这是错的——Pipeline 里的命令不保证原子性中间可能插入其他客户端的命令。而且 Pipeline 一次塞太多命令会撑爆客户端的输出缓冲区服务端有client-output-buffer-limit限制超了会直接断开连接。我一般建议每批 100 到 500 条别一次塞几万条。7. 运维观测分支出事时你会打开的那几个命令这个分支平时不看出事时救命。我的导图上这一块只挂了四组东西慢查询、关键指标、危险命令、常见事故清单。它不需要深但需要快——出事的时候没时间翻文档。7.1 慢查询和延迟监控slowlog-log-slower-than单位是微秒默认 10000也就是 10 毫秒设成 0 记录所有命令设成负数关闭。slowlog-max-len是保留条数默认 128。用SLOWLOG GET 10看最近 10 条每条包含命令、耗时、客户端地址、时间戳。要注意慢查询记录的是命令执行时间不包含网络传输和排队时间所以如果客户端觉得慢但 slowlog 里没有记录问题大概率在网络或者在排队。LATENCY系列命令是 2.8.13 之后加的能监控事件级别的延迟比如LATENCY LATEST看最近的事件、LATENCY RESET清空。这块和 slowlog 是互补的slowlog 看单条命令latency 看系统级事件比如 fork、过期删除、AOF 刷盘。两个都挂在导图上标注各自的适用面。7.2 需要长期盯的指标INFO的输出很长导图上只挑几个必须看的。used_memory和used_memory_rss看内存真实占用mem_fragmentation_ratio看碎片。connected_clients看连接数突然暴涨通常是客户端没正确关闭连接或者连接池配置不对。instantaneous_ops_per_sec看 QPS。keyspace_hits和keyspace_misses算命中率命中率突然掉下来基本就是缓存出问题了。rejected_connections是达到maxclients上限后被拒绝的连接数这个值非零说明连接池该调了。evicted_keys是淘汰的 key 数量持续非零说明内存不够要么扩内存要么查大 key。total_net_input_bytes和输出字节数看流量。这些指标我建议直接截图贴进导图比记名字直观。7.3 危险命令和权限控制KEYS *在生产环境是大忌它一次性遍历所有 keyO(N) 复杂度和阻塞时间随数据量线性增长。用SCAN替代虽然SCAN不保证返回完整快照、可能重复返回但它是增量迭代不会阻塞。FLUSHALL、FLUSHDB更是绝对不能随手敲我见过有人在测试环境敲成生产环境整个库清空。Redis 6.0 之后有 ACL可以按用户限制命令和 key 的访问范围比如给业务账号只开get set hget hset禁用dangerous这类命令组。老版本可以用rename-command把危险命令改个名等于变相关闭。这两条都要挂在运维分支上因为它们是唯一能在服务端层面兜底的手段。7.4 我整理的事故清单导图上这一块是最有价值的个人积累我把自己和周围人踩过的坑列成一个清单遇到问题按清单过一遍命中率挺高。内存突然打满先查evicted_keys再--bigkeys扫描大 key最后看是不是有人写了没设 TTL 的键。响应突然变慢先看SLOWLOG再看LATENCY LATEST然后查是否发生了 forklatest_fork_usec最后看网络和连接数。主从延迟变大看master_repl_offset和从节点的slave_repl_offset差值差值持续增长通常是主节点写入量超过了repl-backlog-size的覆盖能力或者从节点在做全量同步。集群报CLUSTERDOWN先CLUSTER INFO看cluster_state再看是不是有槽没分配重点查cluster-require-full-coverage的配置。连接数暴涨查客户端连接池的maxIdle/maxTotal配置以及有没有代码路径忘了归还连接。8. 导图落地工具、画法和复习节奏骨架和内容讲完了最后说说怎么落地。这块看起来是形式问题但画法不同用起来的效果差很远。8.1 工具选择我试过三种。纯手绘最快A4 纸横着放五分钟能出一个骨架缺点是改起来麻烦、不方便搜索。通用思维导图工具支持 Markdown 大纲导入的那种适合长期维护改一个节点不用重画还能导出图片贴到笔记里。我个人最终用的是先手绘骨架、再导入工具里细化的组合因为手绘阶段思路是发散的工具里的自动布局会限制思路。如果你的导图要做成能分享的形态建议用支持折叠的格式主分支默认折叠需要看细节再展开。一上来全展开几百个节点糊在一起谁都不想看第二眼。分层展示这件事比导图本身内容好不好更重要。8.2 三个画法上的经验第一颜色标记掌握程度。我用三种颜色绿色是我能讲清楚原理和配置的黄色是知道但说不清细节的红色是只听过名字的。画完一遍只看红黄两色复习效率能提高不少。这个做法比按主题分类有用因为复习的痛点在于不知道自己哪里不会。第二每个分支标注一个故障场景。比如持久化分支旁边写机器断电重启后数据丢了多少主从分支写主节点挂了多久能恢复。知识是为了解决问题的把场景写在旁边能防止导图变成名词罗列。第三留白。不要试图一次画完我第一版画了满满一张结果发现全是抄文档没有自己的东西。后来我把每个分支限制在五到八个节点逼着自己做取舍——哪些是真需要记住的哪些查一下就行。取舍本身就是理解的过程。8.3 几个容易画偏的地方一个常见的偏差是把导图当命令手册每个类型下列几十条命令。命令不用记COMMAND命令和官方文档都能查导图上留高频的、易错的、有坑的就够了。另一个偏差是只画不用画完贴到笔记里再也没打开过。我的做法是每月抽十分钟看一遍红色的部分看完能转成黄色的就转一直转不了的说明理解不到位专门去查一次。最后一个偏差是版本。Redis 从 6.0 到 7.x 变化不小ACL、多线程 IO、listpack 替换 ziplist、Function 替代部分 Lua 场景这些都会影响导图内容。我在导图首页写上了版本号每次升级前对照着更新一遍避免用着 7.x 的实例查着 5.x 的资料。我个人在实际操作中的体会是画这张图最大的收获不在于图上有什么而在于画的过程中被迫回答这两个东西到底什么关系。比如持久化和主从到底是什么关系我以前说不太清画图的时候必须把它们摆在同一个纵轴上排层级一摆就清楚了——持久化解决的是进程重启主从解决的是机器故障它们根本不在一个维度上却被很多人当成可替代方案在讨论。类似的摆位置就发现问题的时刻我遇到过好几次这是单纯看文档得不到的。
阅读完成 · 觉得有帮助?