ARTICLE DETAIL

资讯详情

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

Redis内存调优实战:从内存模型到大Key治理

Redis内存调优实战:从内存模型到大Key治理 先聊一个场景你负责的 Redis 晚上突然告警内存从 3GB 直线飙到 9GBGET一个 key 却要 200ms。用redis-cli --bigkeys扫一遍发现有个LIST存了 80 万条历史记录里面塞的全是几百字节的 JSON 字符串。这种事故我在线上见过太多次了原因就一句话Redis 的数据结构看着简单底层的内存模型和编码规则你不搞清楚它早晚给你来个深夜惊喜。这篇文章是《7天学会 Redis》系列的第 6 天专门聊内存和性能调优。如果你已经会基本命令、知道五种数据类型怎么用接下来的内容可能直接决定你能不能应对生产环境的内存告警。我会从内存模型拆解、数据结构编码优化、淘汰策略选型、持久化性能取舍、慢查询与大 key 治理这几个角度把调优这件事拆成一个一个能落地的动作。适合刚接触 Redis 运维的同学也适合被线上 OOM 折磨过的老手。1. 内存去哪儿了Redis 内存模型与开销拆解1.1 先看懂 INFO memory调优第一件事不是改配置是看懂 Redis 到底怎么看待自己的内存。我一直觉得INFO memory的输出是 Redis 给运维同学开的“体检报告”但很多人根本没仔细读过。正常一个 Redis 节点跑一遍这个命令你会看到类似下面的字段# Memory used_memory: 1575787096 used_memory_human: 1.47G used_memory_rss: 1908293632 used_memory_peak: 2184968904 used_memory_peak_human: 2.03G used_memory_lua: 31744 mem_fragmentation_ratio: 1.21 mem_allocator: jemalloc-4.0.3几个核心字段我给你逐个说清楚used_memoryRedis 自己统计并分配的内存总量包含数据占用的空间、key 本身的开销、客户端缓冲区、Lua 脚本等等。单位是字节所以看它的时候配合_human的字段更方便。used_memory_rss操作系统层面看到的进程常驻内存也就是你top看到 RSS 的那个数值。这个值通常比used_memory大因为包含内存碎片、虚拟内存映射这些。used_memory_peak历史峰值是从 Redis 启动到现在的最高内存记录。这个字段很有用判断内存有没有“曾经爆炸过”。mem_fragmentation_ratioused_memory_rss除以used_memory的比值。这个就是我常说的碎片率后面单独展开。mem_allocator内存分配器正常都是 jemalloc这是 Redis 官方默认的。我的经验是每天定时把INFO memory的结果和监控系统打通观察曲线。内存不是你说感觉正常就正常的used_memory_peak如果一直高位不动说明可能有大 key 被删除了但内存没完全还给系统或者说碎片率已经不对劲。1.2 内存开销拆解不是只有数据才占内存很多人有个错觉我在 Redis 里存了 100MB 数据那 Redis 占用的内存就应该是 100MB 左右。这是对 Redis 内存模型最大的误解。我先抛一个结论一个 key 从存进去开始内存开销由几部分组成——数据本身、redisObject、SDS 字符串头、全局哈希表条目、过期字典条目、空闲指针对齐的填充字节。拿字符串类型举例子一个 10 字节的 key 对应一个 20 字节的 value实际消耗的内存可能是 70 到 80 字节多余的这些就是所谓的“固定开销”。具体拆分一下redisObject结构体Redis 中每个 value 都是一个redisObject包含类型、编码、引用计数、指向实际数据的指针。这个结构体在 64 位系统上大概是 16 字节。SDS 字符串Redis 自己实现的动态字符串头部有长度、分配容量、标记位这些字段。一个 100 字节以内的字符串头部还要额外占用 3 到 5 字节加上\0结尾。全局 dict 条目Redis 的键空间是一个大字典每个键值对在这个字典里都有一个条目包含 key 指针、value 指针、下一个节点的指针大概 24 字节以上。过期 key 的额外字典给 key 设置了 TTLRedis 还会在过期字典里维护一条记录这又是开销。所以你可以用一个简单的公式估算单个 key 内存开销 ≈ 16redisObject 字符串头 key 长度 value 长度 dictEntry 开销正是因为有这些固定开销小 value 的内存放大效应非常明显。比如你存 100 万个只有 5 字节的字符串理论数据只有 5MB但实际内存占用可能超过 100MB。这 20 倍的差距没人告诉你的话你排查内存暴涨时会一头雾水。另一个常见坑是共享整数对象Redis 启动时会预创建 0 到 9999 这些小整数对象。如果你大量使用小数字当 value这部分不额外占用内存但如果超过这个范围每个字符串都要独立申请内存。这个知识虽然影响不大但偶尔能解释为什么同样规模的数据内存占用差异这么多。1.3 内存碎片为什么内存看着很浪费继续看INFO memorymem_fragmentation_ratio是调优永远绕不开的指标。这个比值是 RSS 对 used_memory 的比值正常范围大概在 1.0 到 1.5 之间。小于 1危险信号。说明 Redis 使用了 swap内存被换到磁盘了性能会断崖式下跌。检查一下操作系统层面有没有做 swap 配置。1 到 1.5健康区间内存使用效率高。大于 1.5碎片化严重。used_memory看着不高但是 RSS 很高操作系统层面内存已经被撑爆了。常见原因是频繁增删 key、批量写入后大量删除、key 的 value 大小差异巨大。处理碎片我常用的方案有三条重启前先做主动碎片整理。Redis 4.0 以上支持activedefrag yes配置配合active-defrag-ignore-bytes 100mb和active-defrag-threshold-lower 10让 Redis 在后台自动搬移内存配对碎片。生产环境开启前先压测一下因为主动碎片整理本身会消耗 CPU。如果碎片率太高比如超过 2.0且业务允许短暂抖动直接redis-cli shutdown save后重启。RDB 加载过程会重新分配内存碎片会被“清零”。这是最暴力也是最有效的办法。控制 value 大小的均匀性。虽然这个属于数据设计层面但 value 差异大是碎片的主要来源之一能避免尽量避免。内存碎片这个问题最开始学 Redis 时总觉得是玄学后来才明白这本质上是 malloc 和 free 的交替使用导致堆内存无法连续利用。jemalloc 已经算是个好分配器了但你不可能完全消除碎片只能监控它、管理它。2. 数据结构优化省内存从编码开始2.1 五种类型的底层编码你知道你的键在用哪种吗Redis 最良心的地方就是每种数据类型都有不止一种底层编码。你存 10 个整数到 Set 里和存 10 万个字符串到 Set 里底层结构完全不同。这就是为什么同样类型的数据内存占用量差别这么大。用OBJECT ENCODING key可以查出某个 key 当前的编码127.0.0.1:6379 OBJECT ENCODING mylist quicklist 127.0.0.1:6379 OBJECT ENCODING myset intset 127.0.0.1:6379 OBJECT ENCODING myhash listpack把五种类型的常用编码整理成一张表你一眼就能看明白数据类型编码类型触发条件说明Stringintvalue 是整数且在 long 范围内直接用 8 字节存整数最省内存Stringembstrvalue 长度 ≤ 44 字节字符串和 redisObject 连续分配Stringrawvalue 长度 44 字节两段内存分配多一次寻址Listquicklist默认压缩列表 双向链表的组合体Hashlistpack元素少且 value 短紧凑紧凑节省内存Hashhashtable超过阈值转换为真正的哈希表Setintset全部是整数且数量少有序整数数组省内存且支持二分查找Sethashtable超过阈值或含非整数转成哈希表ZSetlistpack元素少且 value 短紧凑编码ZSetskiplist超过阈值跳表 哈希表的组合这里的编码名称在新版本里有变化7.x 以后把 ziplist 换成了 listpack但优化思想是一样的元素少、value 小的时候用连续内存的紧凑数组不用指针链来链接提高缓存命中率降低内存占用。举个例子一个 Hash 只有 3 个 field每个 field 和 value 都在 64 字节以内listpack 编码下所有数据是连续存放的中间没有指针内存紧凑到极致。如果这个 Hash 用哈希表实现光是 3 个 dictEntry 就比全部 listpack 数据本身还大。2.2 压缩列表/紧凑型编码的最大配置阈值既然省内存为什么不所有场景都用紧凑编码因为紧凑结构有性能代价读写时间复杂度是 O(n)。元素少时无所谓元素多了线性扫描就拖垮性能。所以 Redis 用两个参数控制转换时机。相关配置如下我以常见生产模板为例hash-max-listpack-entries 128 hash-max-listpack-value 64 set-max-intset-entries 512 zset-max-listpack-entries 128 zset-max-listpack-value 64entries是数量阈值value是单个元素的最大字节长度。超过任意一个编码就转换成标准结构。比如 Hash 有 200 个 field哪怕每个 value 只有 10 字节也会转成 hashtable。调优思路很简单如果你的业务特征是“字段多但值小”Hash 的hash-max-listpack-entries可以调大比如 256 或 512。但是注意listpack 在HGETALL这种命令下复杂度是 O(N)N 越大单次命令耗时越长。所以不要贪心我见过有人把 entries 调到 1024结果HGETALL一次返回 2MB 数据网络传输和反序列化直接卡死。Set 的 intset 编码是个意外惊喜存整数时intset 不仅省内存查询还快因为底层是排序数组直接二分查找。如果你的业务信息适合用整数 ID 集合比如“用户粉丝 ID 列表”这种尽量全部用整数让 intset 一直生效。2.3 客户端序列化陷阱为什么存个 JSON 多占 70% 内存内存调优不止调 Redis 端客户端怎么序列化和压缩也是大头。这是我和 Java 开发同事一起排查线上内存问题时最常遇到的坑。比如说我们要在 Redis 里存一个用户对象很多人习惯性地把对象直接JSON.toJSONString(user)然后SET user:10001 {name:张三,age:30}。这个字符串看着也就 30 字节没问题。问题是如果对象有几十个字段一个用户对象序列化后可能 300 到 500 字节。这时候如果你有 100 万用户在线光用户数据的裸字符串就占了 400MB 到 500MB。有一个更好的思路把对象先压缩再存。Redis 自带的SETRANGE和 Lua 脚本都没法自动帮你压缩但你可以在应用层用 gzip 或 snappy 压缩后写入读取时再解压。我实测下来JSON 文本的压缩率通常能达到 50% 到 70%也就是说一个 500 字节的对象压缩后只剩 150 到 250 字节。读者可能担心压缩和解压的 CPU 开销其实在 snappy 算法下这个开销远比想象中的内存节省划算。序列化框架的选择同样影响巨大。Java 里面 JDK 原生序列化不仅慢产物还大换成 Kryo 或 protobuf 之后对象体积能再降一半以上。这也是网上热词“redis序列化”被反复讨论的原因。所以说内存优化是端到端的——Redis 本身在省你的应用层也在决定它最终的占用。3. 过期与淘汰让内存有进有出3.1 内存上限不出问题不用注意出问题就是要命很多 Redis 教程从来不说maxmemory因为开发环境下 Redis 默认maxmemory 0意思是“不限制内存”。可到了生产环境Redis 和操作系统其他进程抢内存一旦吃光物理内存操作系统开始 swap然后所有 Redis 请求都在卡这个事故我经历过不止一次。配置内存上限前先列两个问题你的机器总内存多少给操作系统和其他进程留多少余量你的 Redis 实例是单机独享还是和别的进程混部一般建议单机 Redis 的maxmemory设为物理内存的 50% 到 70%。比如一台 16GB 的机器配置maxmemory 8gb或10gb剩余内存给操作系统 page cache 和稳定性兜底。如果你用的是 Redis Cluster每个节点的上限按分片容量来算规则类似。设置完上限之后还有一件很容易被忽略的事maxmemory只约束 Redis 自己的 used_memory不代表 RSS 一定不超过它。碎片率会让 RSS 超过 limit所以最终选 limit 时要再留出 20% 的余量给碎片和临时缓冲区。3.2 八种淘汰策略的前世今生和选择设了maxmemory内存达到上限后新写入怎么办这就轮到maxmemory-policy出场。Redis 提供的策略有策略含义适用场景noeviction不淘汰写命令直接报错不能丢任何数据的业务比如支付状态allkeys-lru从所有 key 中按最近最少使用淘汰缓存场景无差异化的全局热数据volatile-lru从已设置 TTL 的 key 中按 LRU 淘汰缓存 部分业务持久混合allkeys-lfu从所有 key 中按使用频率淘汰热点集中、访问倾斜明显的缓存volatile-lfu从已设置 TTL 的 key 中按 LFU 淘汰需要淘汰但不碰无 TTL 的 keyallkeys-random所有 key 随机淘汰数据热度相似、无差别缓存volatile-random已设置 TTL 的 key 随机淘汰热点不明显的临时数据volatile-ttl优先淘汰剩余 TTL 最短的 key接近过期的 key 先被清理选择哪个核心问题就一个能不能丢数据如果 Redis 是纯缓存推荐allkeys-lru或allkeys-lfu。LFU 出现在 Redis 4.0 之后解决的是“某个 key 曾经热过但再也不用LRU 却总淘汰不掉它”的问题。如果你的访问曲线有明显热点且持续变化LFU 比 LRU 更准。如果 Redis 里混着缓存和持久数据选volatile-lru会安全些因为它不碰那些没有 TTL 的数据。绝对不能接受逐出导致丢失那就noeviction但你要有完整的内存监控和告警一旦内存打满写请求就失败你需要立刻介入。我个人的生产经验绝大多数缓存场景maxmemory-policy allkeys-lru配合合理的容量规划就足够了。过度复杂的策略组合带来的收益和运维成本不成正比。3.3 过期删除的两种机制与缓存雪崩关系Redis 的过期 key 删除不是定时器实时扫的而是两种机制配合惰性删除和定期删除。惰性删除访问某个 key 时发现它已经过期顺手删除。缺点是过期 key 如果一直不被访问就一直在内存里躺着。定期删除Redis 后台周期从设置了过期时间的 key 中随机抽一批检查是否过期过期就删。默认每秒运行 10 次左右每次处理耗时上限受hz配置影响。这个机制会导致一个现象大量 key 同时过期时因为定期删除只能按批次处理Redis 的内存并不会立刻下降而是在几秒甚至几十秒内逐步下降。如果在这个过期内内存打满就可能触发淘汰策略。缓存雪崩就是这样产生的假设你把所有缓存都设置了 1 小时过期且过期时间集中在 12 点整。那一瞬间 Redis 需要删除海量 key删除不是大问题大问题是——下一次请求来了发现缓存没有全部穿透到数据库数据库连接数打满整个系统雪崩。规避办法我试过几种比较靠谱的过期时间加随机偏移expire 基础时间 random(0, 300)让过期时间在 5 分钟内均匀散开。热点数据不设过期设置逻辑过期时间在应用层判断字段值是否过期再异步刷新缓存。这个方案能彻底避免集中过期代价是实现复杂度上来了。双层缓存本地缓存 Redis 缓存即使 Redis 缓存雪崩本地缓存还能顶一小段时间。顺着“缓存雪崩”经常一起出现的是“缓存穿透”和“缓存击穿”这两个问题的核心不在内存而在对数据库的压力管理。缓存穿透是说请求的 key 根本不存在于缓存和数据库每次都绕过了缓存缓存击穿是说一个高热 key 在过期的一瞬间大量请求同时怼到数据库。处理穿透可以缓存空值加短 TTL或者用布隆过滤器挡一下击穿则可以加互斥锁即那个著名的“Redis 分布式锁”或者逻辑过期方案。这些都是 Day 4 讲缓存时应该细聊的这里主要提醒你内存管理的几个策略不是相互独立的你需要在设计阶段就联动考虑。4. 持久化耗性能AOF 和 RDB 的调优路4.1 RDB 的 fork 是谁在付钱RDB 持久化是 Redis 通过 fork 子进程生成的快照。fork瞬间复制父进程的页表这个过程虽然理论上是写时复制但页表本身的大小和内存量成正比。内存越大fork 越慢阻塞时间越长。这就是为什么 12GB 内存的 Redis 在开启bgsave时主进程可能卡 100ms 以上这是我调优过程中最痛的一课。影响 RDB 性能的配置项save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yessave的三行配置定义触发快照的条件900 秒内至少 1 次写、300 秒内至少 10 次写、60 秒内至少 1 万次写。stop-writes-on-bgsave-error yes的意思是如果上次 bgsave 失败Redis 停止接收写命令避免数据丢失风险扩大。线上保持 yes 更安全。rdbcompression能让 RDB 文件更小消耗少量 CPU一般保持默认。rdbchecksum在加载时会做校验增加几十毫秒加载耗时但能防止文件损坏纠错不建议关。我的经验是调 RDB 不是调快照频率而是调快照大小和 fork 时的内存冗余。在内存占比高的实例上RDB 快照尽量放到业务低峰期且保证机器有足够闲置内存。比如 Redis 使用了 8GB那 fork 后写时复制可能临时多占 1 到 2GB 内存如果机器只有 12GB这段时间就有 OOM 风险。4.2 AOF 三种刷盘策略怎么取舍AOF 持久化把写命令追加到文件里。默认appendfsync everysec这是 Redis 权衡性能和可靠性的标准答案。它的含义是一秒钟把缓冲区里的内容同步到磁盘极端情况下可能丢最近 1 秒的数据。另两个选择策略数据安全性性能影响适用场景always每次写命令都 fsync基本不丢数据写性能骤降碎盘直接没法用金融级严格对账场景everysec每秒 fsync 一次最多丢 1 秒数据性能折中生产首选绝大多数线上业务no由操作系统决定何时刷盘最高性能可接受较大丢数据的内部缓存everysec是生产环境最常用的配置但不是没坑。如果磁盘性能差或者瞬间写入量过大可能 AOF 同步队列积压导致主线程阻塞。你会看到日志里出现Asynchronous AOF fsync is taking too long或者Cant recover from AOF sync error这就是磁盘写不过来了。另一个 AOF 相关的可口优化是aof-use-rdb-preamble yes。开启后AOF 重写生成的混合格式文件先用 RDB 二进制快照再追加增量命令既保留了 AOF 的完整性又利用了 RDB 的加载速度。最好保持默认 yes。4.3 AOF 重写为什么会放大内存压力AOF 文件无限增长不是好事所以 Redis 提供了 AOF 重写机制把当前内存里的数据重新生成一份最小化的写命令集合。配置项长这样auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb含义是当 AOF 文件大小超过上次重写后大小的 100%即翻倍且文件大于 64MB 时触发自动重写。重写也是 fork 子进程来完成和 RDB 一样有 fork 停顿开销。重写过程中父进程继续接收新写命令会把新命令积攒在重写缓冲区里这又额外占用内存。这个特性最常翻车的场景就是内存已经很紧张AOF 又触发重写fork 直接因内存不足失败随后stop-writes-on-bgsave-error和重写失败连环爆炸。所以我现在的做法是监控INFO persistence中的aof_rewrite_in_progress和rdb_bgsave_in_progress不允许手动执行 debug 级别的重写。高内存水位时比如超过 70%主动拆 key、扩大集群而不是依赖 AOF 重写自己收拾残局。Redis 6.0 之后推出了aof-timestamp-enabled这种增量支援机制但实际生产中使用率不高暂时不用投入太多精力。5. 性能定位慢日志和大 key 治理5.1 慢日志怎么配怎么用内存和性能调优最先要搞清楚的是“谁拖慢了 Redis”。Redis 自带了慢日志机制不像 MySQL 那样需要装插件它本身就把每条命令的执行耗时记录下来按阈值切分。slowlog-log-slower-than 10000 slowlog-max-len 128 slowlog-log-max-len 1024slowlog-log-slower-than单位是微秒10000 即 10ms。生产环境建议设置为 50005ms甚至 3000这样能抓到更多潜在的慢命令。slowlog-max-len控制最多保留多少条记录。查看慢日志127.0.0.1:6379 SLOWLOG get 5结果里每个条目包含命令执行时间戳、消耗时长、命令详情。我处理慢日志时有一个固定套路连续跑几次SLOWLOG get看慢命令的类型和时间分布。如果是KEYS、SMEMBERS这种 O(N) 命令频繁出现直接考虑改SCAN替代。如果是HGETALL、LRANGE这种看返回的数据量是不是太夸张。如果是周期性出现和 RDB 快照、AOF 重写的时间点对上那就是 fork 阻塞问题在持久化侧不在命令本身。Redis 还提供了 latency monitor 功能开启后能监测各种事件的延迟峰值latency-monitor-threshold 100配合LATENCY HISTORY命令看到某个时间点命令执行延迟飙升到多少、持续多久。这个在日常排障中比慢日志更直观因为它能画出时间线和 trace 系统配合定位。5.2 Redis 自带工具--bigkeys 和 --memkeys大 key 是 Redis 性能怒火最主要的来源。一个 1MB 的 keyGET它和GET一个 10 字节的 key网络传输量完全不是一个级别。而且大 key 在淘汰、过期、删除时会阻塞主线程很多“Redis 直接卡死几秒”的事故最后都定位到某个大 key 上。找大 key最省事的方式是用官方工具# 统计各种类型中最大的 key redis-cli --bigkeys # 按内存占用排序统计Redis 4.0 支持 redis-cli --memkeys--bigkeys的实现原理是遍历所有 key对每种类型抽样字符串类型直接看长度列表/哈希/集合/有序集合类型则循环取元素数量。它会输出每个类型下最大的 key以及每个类型的元素总量统计。注意这不是准确的内存测量它只是给出一个“谁可能是大麻烦”的清单单靠它还不够。更精确的测量单 key 内存用MEMORY USAGE127.0.0.1:6379 MEMORY USAGE bigkey输出的是这个 key 实际占用的字节数会考虑编码、序列化长度、redisObject 开销等。生产环境批量排查时用redis-cli --memkeys扫一遍再精确追踪大 key 的详细信息。另外如果你会SCAN也可以自己写脚本遍历所有 key配合DEBUG OBJECT或者MEMORY USAGE拿到更多维度。我自己写过一个 Python 脚本定期把大 key 清单推到日志系统低于历史峰值就从列表移除。这是治理大 key 最有效的手段——让大 key 在出现的一周内就被发现而不是等它撑满内存时才知道。5.3 热 key 与大 key 的处理策略找到大 key 之后怎么处理取决于它的类型字符串大 key如果是缓存数据可以压缩后存储如果是业务数据考虑拆成多个 key分散压力。例如用户的大 JSON 拆成多个 Hash field读取时只取需要的字段。Hash 大 key把一个大 Hash 按业务维度拆成多个中等 Hash每个存较少的 field减少HGETALL的返回量。List 大 key这是最常见的坑尤其是消息队列场景。建议按容量或时间进行分段比如每天一个 list读取时只取当天的避免一次性 LRANGE 整个队列。Set/ZSet 大 key如果业务允许用分片 key 或换用别的结构。ZSet 的按分值区间查询在大 key 下性能很差。热 key 则是另一个维度的性能毒瘤。一个 key 一秒被访问 10 万次单个节点单线程的特殊性这个 key 会拖慢后面所有命令。Redis 单线程指处理命令的是一个线程虽然 6.0 之后网络 IO 是多线程但命令执行还是串行的。所以热 key 的优化思路通常是在应用层加本地缓存如 Caffeine把热 key 的读写挡在 Redis 之前或者把热 key 复制出多个带后缀的副本 key分散读压力。我对大 key 和热 key 有个统一的治理原则不要让任何一个单独的 key成为 Redis 节点的单点瓶颈。这个原则写进代码评审 checklist 里比出了事再救火有用得多。6. 常见问题速查与个人体检清单6.1 线上典型问题与排查思路把我这几年遇到的高频问题整理成速查表方便你直接对照现象可能原因排查动作内存飙升后不下降大 key 过期删除时阻塞碎片率高INFO memory看碎片率SLOWLOG get看 DEL 耗时长碎片超过 1.5 考虑重启写请求超时AOF 刷盘过慢或 fork 阻塞看日志里有无Asynchronous AOF fsync调appendfsync策略避开持久化高峰缓存击穿/雪崩过期时间集中、热点 keyINFO stats看expired_keys变化调整过期时间随机化used_memory不高但机器 OOM内存碎片 fork 子进程内存mem_fragmentation_ratio过高降低 maxmemory 留出余量KEYS 命令导致卡顿O(N) 扫描全局 key排查慢日志全部改用 SCAN 命令集群节点内存不均匀数据倾斜大 key 存在单节点用--memkeys找出大 key拆分或迁移上面每一条都是真实项目里踩过的坑。我最想强调的就是第一行内存飙升后不下降很多时候不是内存泄漏而是used_memory下降但used_memory_rss还挂着不动。碎片率看着 1.3、1.4你以为还能忍结果再来一次大 key 过期峰值内存就爆了。遇到这种情况该重启果断重启业务抖动 5 秒和内存爆掉宕机 10 分钟你选一个。6.2 个人体检清单生产 Redis 上线前过一遍如果你负责的 Redis 要上线我建议至少守着这套清单走一遍maxmemory设置了没有是否留了 20% 余量maxmemory-policy选的是不是和业务匹配缓存场景是不是 allkeys-lruINFO memory的碎片率在 1 到 1.5 之间吗有没有定期跑redis-cli --bigkeys --memkeys排查大 keyAOF 刷盘策略是不是 everysecaof-use-rdb-preamble yes开启了吗过期的缓存 key 有没有加随机偏移有没有开启慢日志监控告警阈值合不合理持久化和高并发写的时间窗口是否会互相叠加这套清单我每次上线新业务都会过一遍省下来的排障时间远超写的时间。你如果能把第 5 天学的主从复制、哨兵和高可用架构和第 6 天的内存调优组合起来基本上就能撑起一个小型公司的 Redis 运维盘子了。最后提醒一句Day 7 我们通常会聊 Redis Cluster 集群的搭建和运维。但在进集群之前先把单机内存这关过了。单机表现都不好集群只会把单点问题放大成多点问题。今天的内容我建议你直接在测试环境把OBJECT ENCODING和MEMORY USAGE挨个试一遍看着真实数据变化比背结论有效得多。
返回列表