news 2026/10/23 9:36:04

一张Redis思维导图:串起持久化、缓存与并发原子性

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
一张Redis思维导图:串起持久化、缓存与并发原子性

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默认 128
Hashlistpackhashtable-hash-max-listpack-entries128 /hash-max-listpack-value64
Setintsetlistpackhashtableset-max-intset-entries512
ZSetlistpackskiplist-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 yes,4.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×4+1=9 种,实际上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_ratio(used_memory_rss / used_memory),1 到 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 30000,NX保证互斥,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 *在生产环境是大忌,它一次性遍历所有 key,O(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,然后查是否发生了 fork(latest_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 的资料。

我个人在实际操作中的体会是,画这张图最大的收获不在于图上有什么,而在于画的过程中被迫回答"这两个东西到底什么关系"。比如持久化和主从到底是什么关系,我以前说不太清,画图的时候必须把它们摆在同一个纵轴上排层级,一摆就清楚了——持久化解决的是进程重启,主从解决的是机器故障,它们根本不在一个维度上,却被很多人当成可替代方案在讨论。类似的"摆位置就发现问题"的时刻,我遇到过好几次,这是单纯看文档得不到的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/23 9:31:55

数字编码解析:26.1.20的日期与版本号应用

1. 项目概述"26.1.20"这个看似简单的数字组合&#xff0c;实际上蕴含着丰富的解读空间。作为一名长期关注数据编码和日期格式的研究者&#xff0c;我发现这类数字串在实际应用中往往被忽视其潜在价值。今天我们就来深入探讨这个数字组合可能代表的含义及其应用场景。…

作者头像 李华
网站建设 2026/10/23 9:29:10

Dynamics 365与IoT集成:从传感器到工单的无人干预运维闭环

看到“无人干预”这四个字&#xff0c;先别急着激动。我做集成这几年&#xff0c;最大的体会是&#xff1a;真正能落地的无人值守&#xff0c;不是把人全部撤掉&#xff0c;而是把人在现场判断的活儿&#xff0c;用一套可靠的流程和数据结构化地做掉。Dynamics 365 加上 IoT&am…

作者头像 李华
网站建设 2026/10/23 9:27:30

同步量测+主动配电网故障诊断:从单点保护到全网协同

手头那套老配网故障定位逻辑&#xff0c;在并了几路分布式光伏之后开始各种闹脾气。不是误报就是漏报&#xff0c;最夸张的一次&#xff0c;馈线末端实际没故障&#xff0c;系统却根据故障电流方向判断出“倒送电”&#xff0c;把下游开关方向标反了。后来我才彻底想明白&#…

作者头像 李华
网站建设 2026/10/23 9:26:58

AI批量抠图全流程指南:如何一键搞定透明背景图片

批量抠图这事&#xff0c;我以前也觉得非PS不可。直到有一次要处理八十多张商品图&#xff0c;一张张用钢笔工具勾路径&#xff0c;勾到凌晨两点还剩下三十张&#xff0c;第二天眼睛看屏幕都是重影&#xff0c;才下定决心找别的路子。后来试过好几款一键抠图工具&#xff0c;坦…

作者头像 李华
网站建设 2026/10/23 9:26:52

MATLAB相机标定源码解析:从角点检测到非线性优化全流程

简介&#xff1a;这份 MATLAB 标定工具源代码面向从事相机标定、立体视觉与鱼眼镜头校正的开发者与研究人员&#xff0c;尤其适合需要复现经典标定流程、研究优化迭代算法的中高级用户。资源包共 186 个文件&#xff0c;以 182 个 .m 脚本为主体&#xff0c;涵盖标定优化迭代、…

作者头像 李华
网站建设 2026/10/23 9:26:17

PDF批量压缩实战:Ghostscript与PyMuPDF脚本方案详解

PDF 压缩这件事&#xff0c;绝大多数人第一次接触是从“文件太大传不出去”开始的。单个文件还好说&#xff0c;随便找个在线工具就能解决&#xff1b;但如果手上是几十个、上百个 PDF&#xff0c;逐个上传下载&#xff0c;既慢又容易被各种网站限制大小和次数。真正的问题不是…

作者头像 李华