Redis 集群
Redis 集群
1. 说一下 Redis 的集群方案
Redis 集群方案是指通过部署多个 Redis 节点,并在节点之间进行数据复制、故障转移或数据分片,以提高系统的:
- 可用性;
- 读写性能;
- 数据容量;
- 故障恢复能力。
单机 Redis 主要存在三个问题:
- 单点故障:节点宕机后,服务不可用;
- 性能瓶颈:所有请求都由一个节点处理;
- 容量瓶颈:数据量受单机内存限制。
针对这些问题,Redis 原生提供了三种常见部署方案:
- 主从复制;
- Sentinel 哨兵模式;
- Redis Cluster 集群模式。
其中,主从复制主要解决数据冗余和读扩展问题,Sentinel 主要解决自动故障转移问题,Redis Cluster 进一步解决数据分片和水平扩容问题。
主从复制
主从复制现在通常称为主节点与副本节点复制,旧版本文档中也称为 Master-Slave。
一个主节点可以对应多个副本节点:(主备)

主从复制主要解决:
- 单机没有数据副本的问题;
- 读请求过多导致主节点压力过大的问题;
- 为故障恢复和高可用提供数据基础。
工作原理

客户端通常将写请求发送到主节点,主节点执行写命令后,将数据变更复制到副本节点。
Redis 默认采用异步复制:
客户端写入 Primary -> Primary 执行并返回结果 -> 异步将写命令复制给 Replica
副本节点首次连接主节点时,一般需要进行全量同步;连接中断后重新连接时,会优先尝试部分同步。如果无法进行部分同步,则重新进行全量同步。
副本节点默认处于只读模式,可以用于处理允许短暂数据延迟的读请求。
优点
- 保存数据副本,提高数据冗余能力;
- 可以进行读写分离,扩展读性能;
- 可以将部分只读查询分配到副本节点;
- 是 Sentinel 和 Redis Cluster 实现高可用的基础。
缺点
- 裸主从复制不会自动完成故障转移;
- 主节点故障后,通常需要人工提升副本并修改客户端连接;
- 所有节点保存相同的数据,不能突破单机数据容量上限;
- 异步复制存在延迟,副本可能读到旧数据;
- 主节点在数据尚未复制到副本时故障,可能丢失已确认的写入。
WAIT 命令可以要求一定数量的副本确认收到写入,从而降低数据丢失概率,但它不能让 Redis 变成严格的强一致性系统。
Sentinel 哨兵模式
Sentinel 是建立在主从复制之上的高可用方案。
除了主节点和副本节点外,还会部署多个 Sentinel 进程:

普通主从复制虽然有数据副本,但主节点故障后无法自动恢复服务。
Sentinel 主要解决:
- 自动发现主节点故障;
- 自动选择并提升新的主节点;
- 将其他副本切换到新主节点;
- 通知客户端当前主节点地址发生变化。
Sentinel 的主要能力
监控
Sentinel 持续检查主节点、副本节点以及其他 Sentinel 是否正常工作。
故障判断
单个 Sentinel 认为主节点不可用时,属于主观下线。
当达到配置的 Sentinel 数量,即 quorum,都认为主节点不可用时,才会将其判断为客观下线,并尝试发起故障转移。
自动故障转移
主节点被确认下线后,Sentinel 会:
- 在副本节点中选择一个合适的节点;
- 将它提升为新的主节点;
- 让其他副本复制新的主节点;
- 更新并传播新的主从配置;
- 向支持 Sentinel 的客户端提供新主节点地址。
配置发现和通知
客户端可以向 Sentinel 查询当前主节点地址,而不必把固定主节点地址写死在配置中。Sentinel 也可以发布故障和切换事件通知。
优点
- 自动完成故障检测和主从切换;
- 避免主节点成为长期单点故障;
- 客户端可以动态发现当前主节点;
- 不需要对数据进行分片,部署复杂度低于 Redis Cluster。
缺点
- 不支持数据分片,所有数据仍需要存放在单个主节点的内存中;
- 写请求主要仍由一个主节点处理,无法水平扩展写能力;
- 故障转移期间会出现短暂不可用;
- 异步复制下仍可能丢失少量已确认写入;
- 客户端必须支持 Sentinel 服务发现和重连;
- 网络分区和错误配置仍可能带来数据一致性风险。
生产环境通常至少部署 3 个 Sentinel,并尽量放置在故障相互独立的机器或可用区中。quorum 用于判断客观下线,真正执行故障转移还需要获得多数 Sentinel 的授权。
Redis Cluster 集群模式
Redis Cluster 是 Redis 原生的数据分片和高可用方案。
它将全部键空间划分为 16384 个哈希槽,每个主节点负责其中一部分槽:
Primary A:槽 0 ~ 5460
Primary B:槽 5461 ~ 10922
Primary C:槽 10923 ~ 16383
Key 对应槽位的基本计算方式为:
HASH_SLOT = CRC16(key) mod 16384
每个主节点通常还会配置一个或多个副本节点:
Primary A ── Replica A1
Primary B ── Replica B1
Primary C ── Replica C1

Redis Cluster 主要解决:
- 单个节点内存不足,无法保存全部数据;
- 单个主节点无法承担全部读写请求;
- 需要横向增加节点扩展容量和性能;
- 分片节点故障后需要自动切换。
工作原理
客户端根据 Key 计算哈希槽,并将请求发送到负责该槽的主节点。
如果请求发送到了错误节点,Redis 会通过 MOVED 或 ASK 响应告诉客户端应该访问哪个节点。支持 Cluster 的客户端会缓存“槽—节点”映射,后续直接访问正确节点。
每个主节点只负责自己的槽及对应数据。因此,不是任意节点都能直接处理任意 Key 的写请求。
故障转移
如果某个主节点故障,并且它存在可用的副本节点,集群可以将其中一个副本提升为新主节点,继续负责原来的哈希槽。
如果负责某些槽的主节点及其副本全部不可用,那么这些槽就无法继续提供服务;默认配置下,集群可能因此进入不可用状态。
优点
- 数据分散在多个主节点上,突破单机内存限制;
- 多个主节点可以并行处理请求,扩展读写能力;
- 支持增加和移除节点;
- 支持在线迁移哈希槽;
- 配合副本节点可以实现自动故障转移;
- 没有中心代理节点,客户端可以直接访问数据节点。
缺点
- 部署、监控和运维复杂度高于主从和 Sentinel;
- 客户端必须支持 Redis Cluster 协议以及
MOVED、ASK重定向; - 多 Key 命令、事务和 Lua 脚本通常要求相关 Key 位于同一个哈希槽;
- 可以使用 Hash Tag,例如
{user}:1、{user}:2,让多个 Key 落到同一槽; - 数据迁移和故障转移期间可能产生额外延迟;
- 使用异步复制,不能保证强一致性,特定故障场景下可能丢失已确认写入;
- 副本读需要显式使用只读模式,并且可能读到旧数据。
Redis Cluster 会自动负载均衡吗?
Redis Cluster 支持数据分片、槽迁移和重新分片,但不能简单理解为会持续自动均衡所有节点的数据和请求。
添加新的主节点后,新节点最初没有负责的槽,也没有数据。需要通过 redis-cli --cluster reshard、redis-cli --cluster rebalance 或相应的运维平台,将部分槽迁移到新节点。
此外,哈希槽数量均衡也不代表内存和请求压力一定均衡,因为不同槽中的数据量和访问热度可能不同。
生产环境中常见的最小高可用结构是 3 个主节点加 3 个副本节点,共 6 个 Redis 节点。
三种方案对比
| 方案 | 数据分片 | 自动故障转移 | 读扩展 | 写扩展 | 突破单机容量 |
|---|---|---|---|---|---|
| 主从复制 | 不支持 | 不支持 | 支持 | 不支持 | 不支持 |
| Sentinel | 不支持 | 支持 | 支持 | 不支持 | 不支持 |
| Redis Cluster | 支持 | 支持 | 支持 | 支持 | 支持 |
工程中如何选择合适的集群方式?
Redis 的部署方式没有绝对的好坏,核心是看业务需要解决什么问题:数据备份、服务高可用,还是容量和性能的水平扩展。
场景一:只需要数据副本,可以接受人工切换 —— 选择主从复制
如果业务规模比较小,当前主要担心的是 Redis 主节点故障后数据没有副本,而对自动故障恢复要求不高,可以使用主从复制。
典型场景:
- Redis 数据可以完全放在一台机器的内存中;
- 所有写请求由一个主节点承担即可;
- 希望通过从节点保存数据副本,提高数据可靠性;
- 希望把部分读请求分配到从节点,实现简单的读写分离;
- Redis 出现故障时,可以接受人工将某个从节点提升为新的主节点。
例如:
业务规模较小,单机容量足够,写压力不大,主要需求是数据备份+读写分离
这种场景可以选择主从复制
需要注意的是,主从复制本身主要解决数据副本问题,并不能自动完成故障转移。如果主节点宕机,通常需要人工选择新的主节点并修改客户端连接。
场景二:单机容量够用,但不能接受 Redis 长时间不可用 —— 选择 Sentinel
如果 Redis 的数据量和写入压力仍然可以由一个主节点承担,但业务已经要求较高的可用性,希望主节点故障后系统能够自动恢复,那么适合使用 Sentinel。
典型场景:
- Redis 数据仍然能够放进单个主节点;
- 单个主节点能够承担全部写请求;
- 不需要进行数据分片;
- 业务不能接受主节点宕机后人工处理较长时间;
- 希望 Redis 主节点故障后自动选择一个从节点成为新的主节点;
- 希望在高可用和系统复杂度之间保持相对简单的方案。
例如:
单机容量足够,单机写性能也足够,但是业务要求高可用,希望主节点宕机后自动切换
因此,Sentinel 可以理解为 主从复制 + 故障检测 + 自动故障转移
它解决的是高可用问题,但并没有解决 Redis 的容量和写性能水平扩展问题。正常情况下,写请求依然集中在一个主节点上。
场景三:单机容量或性能已经成为瓶颈 —— 选择 Redis Cluster
如果业务规模继续增长,一台 Redis 已经无法保存全部数据,或者单个主节点已经无法承担所有请求,此时仅增加从节点或者使用 Sentinel 都不能解决问题,需要使用 Redis Cluster。
典型场景:
- Redis 数据量已经接近或超过单机能够提供的内存容量;
- 数据量会持续增长,希望以后可以继续增加节点;
- 单个主节点的写入能力已经成为系统瓶颈;
- 希望多个主节点共同承担数据和请求;
- 需要同时获得数据分片、高可用和水平扩展能力;
- 能够接受 Cluster 带来的客户端路由、槽位迁移以及部分多 Key 操作限制。
例如:
业务规模持续增长,单机容量不够或者单机吞吐量不够,需要把数据分散到多个主节点,需要继续增加机器实现水平扩展,Redis Cluster
Redis Cluster 会把数据划分到不同的主节点:

这样解决的不只是高可用问题,更重要的是解决了单机容量和单机性能上限。
实际工程中,可以按照下面这个思路快速判断:

所以三种方案最核心的区别可以概括为:
| 业务场景 | 推荐方案 | 主要解决的问题 |
|---|---|---|
| 单机够用,只需要数据副本,可以接受人工切换 | 主从复制 | 数据备份、读扩展 |
| 单机够用,但要求故障后自动恢复 | Sentinel | 高可用、自动故障转移 |
| 单机容量或吞吐量已经不够,需要多节点共同承担数据 | Redis Cluster | 数据分片、高可用、水平扩展 |
工程上最常见的判断方式其实就是一句话:
单机够用,看是否需要自动故障转移;单机已经不够用,则考虑 Redis Cluster。
汇总总结
Redis 常见的原生集群方案包括主从复制、Sentinel 和 Redis Cluster。主从复制通过异步复制提供数据副本和读扩展,但不能自动故障转移;Sentinel 在主从复制基础上增加监控、服务发现和自动故障转移,但不能解决单机容量与写性能瓶颈;Redis Cluster 通过 16384 个哈希槽将数据分布到多个主节点,并结合副本实现高可用,能够水平扩展容量和读写性能,但部署运维更加复杂,也不保证强一致性。
参考资料
2. 如何通过缓存分层提升系统整体性能?
缓存分层是指在数据源前设置多级缓存,让请求按照“速度从快到慢、容量从小到大”的顺序逐级查询。
常见的二级缓存结构是

- L1 本地缓存:位于应用进程内,例如 Caffeine,访问时不需要网络通信,速度最快,但容量较小,并且不同应用实例之间的数据相互独立。
- L2 Redis 缓存:由多个应用实例共享,容量更大、数据相对统一,但每次访问都需要经过网络。
- 数据库:权威数据源,负责持久化数据,但访问成本通常最高。
缓存分层解决什么问题?
如果所有请求都直接访问 Redis,虽然比访问数据库快,但仍然存在:
- 网络通信和序列化开销;
- Redis 连接和带宽压力;
- 热点 Key 被大量重复访问;
- Redis 故障或抖动时,大量请求直接回源数据库。
引入本地缓存后,最热点的数据可以直接在应用进程内返回,从而:
- 降低请求延迟;
- 减少 Redis 的 QPS 和网络流量;
- 降低数据库压力;
- 在 Redis 短暂抖动时提供一定的缓冲能力。
需要注意,多级缓存提升了性能,但也增加了数据一致性和运维复杂度。
数据应该如何分层?
哪些数据适合放到本地缓存呢?
本地缓存比较适合:
- 访问频率高;
- 数据体积小;
- 更新频率低;
- 可以容忍短时间不一致;
- 被同一个应用实例反复访问。
例如:
- 字典数据;
- 地区、类别等基础配置;
- 热门商品的部分展示信息;
- 路由规则和功能开关;
- 更新频率较低的用户基础信息。
本地缓存并不是实时性要求越高越适合。恰恰相反,由于多实例之间存在失效通知延迟,本地缓存通常更适合能够容忍短暂不一致的数据。
用户登录态、权限、账户状态、库存和余额等数据需要谨慎放入本地缓存。
例如,如果业务要求用户退出登录或账号冻结后立即生效,仅依靠本地缓存的 TTL 可能继续接受已经失效的身份信息。
适合放入 Redis 的数据
Redis 比较适合:
- 需要由多个应用实例共享的数据;
- 数据规模较大,无法在每个进程中保存完整副本的数据;
- 更新相对频繁的数据;
- 需要统一失效和集中管理的数据;
- 分布式锁、计数器、排行榜等需要 Redis 原子操作的数据。
读取流程
通常采用 Cache Aside 模式,读取顺序如下:

具体流程为:
- 先查询本地缓存;
- 本地缓存命中,直接返回;
- 本地缓存未命中,查询 Redis;
- Redis 命中,将结果写入本地缓存并返回;
- Redis 也未命中,查询数据库;
- 将数据库结果写入 Redis和本地缓存;
- 返回结果。
这样,访问频率最高的数据由本地缓存承担,普通热点数据由 Redis 承担,只有两级缓存都未命中时才访问数据库。
如何更新多级缓存?
数据库通常应作为权威数据源。业务数据更新时,不建议简单地同时修改数据库、Redis 和所有应用实例中的本地缓存,因为多个系统之间无法通过普通数据库事务保证原子性。
比较常用的更新流程是:
1. 更新数据库
2. 数据库事务提交
3. 删除 Redis 缓存
4. 通知所有应用实例删除本地缓存
下一次读取时,再从数据库逐级重建缓存。
为什么通常删除缓存,而不是更新缓存?
删除缓存具有以下优点:
- 删除操作简单且幂等;
- 减少并发写入导致旧值覆盖新值的风险;
- 不需要在多个缓存层重复实现数据计算逻辑;
- 下一次读取会从权威数据源重新加载数据。
此时又有人要问了如何让所有本地缓存失效?
因为每个应用实例都有独立的本地缓存,所以只删除当前实例的缓存是不够的。
工程上常见方案包括:
消息队列或 Pub/Sub
数据更新后发布缓存失效事件,所有应用实例收到消息后删除对应的本地缓存。
需要处理:
- 消息丢失;
- 重复消费;
- 消费延迟;
- 新实例启动时没有收到历史消息。
因此,失效操作要保证幂等,并使用 TTL 作为兜底。
Redis Client-side Caching
Redis 6.0 开始提供 CLIENT TRACKING,支持服务端辅助的客户端缓存。
Redis 可以记录客户端读取过哪些 Key。当某个 Key 被修改、删除、过期或淘汰时,向相关客户端发送失效通知,客户端收到通知后删除本地副本。
如果用于接收失效消息的连接断开,客户端应清空本地缓存,避免因为漏掉失效消息而长期读取旧数据。实际使用前还需要确认客户端是否完整支持该功能。
Binlog 或 CDC
监听数据库变更日志,在数据库事务提交后发送缓存失效事件,统一删除 Redis 和各应用实例中的本地缓存。
这种方式与业务代码耦合较低,但需要保证事件投递、消费重试和监控的可靠性。
缓存分层的缺点
缓存分层并不是层数越多越好,它会带来以下问题:
一致性更加复杂
一条数据可能同时存在于:
- 数据库;
- Redis;
- 多个应用实例的本地缓存。
更新时任何一个环节失效或延迟,都可能导致读取到旧数据。
本地缓存重复占用内存
每个应用实例都会保存一份热点数据。实例数量越多,重复占用的总内存越大。
冷启动问题
应用重启或扩容后,本地缓存为空,大量请求会集中访问 Redis。Redis 失效时还可能继续回源数据库。
可以通过预热核心热点数据、限流和请求合并来降低影响。
热点漂移
不同实例接收到的流量不同,本地缓存的热点数据也可能不同,因此需要根据真实命中率和内存使用情况调整容量。
失效通知存在成本
更新特别频繁的数据会产生大量失效消息,本地缓存刚写入就被删除,收益可能低于维护成本。
因此,不应将高频变化的计数器、实时排行榜等数据盲目放入本地缓存。
推荐实现方案
对于大多数读多写少的业务,可以采用:
Caffeine 本地缓存 + Redis 分布式缓存 + 数据库
并配合:
- Cache Aside 读取模式;
- 数据库更新后删除 Redis;
- 通过消息或
CLIENT TRACKING使本地缓存失效; - 本地缓存使用较短 TTL 和容量上限;
- Redis 使用较长 TTL,并加入随机过期时间;
- 热点 Key 使用请求合并或互斥重建;
- 通过版本号防止旧数据覆盖新数据;
- 通过重试、CDC 或 Outbox 保证失效事件最终送达;
- 监控各级缓存命中率、加载耗时和回源次数。
总结
缓存分层是将本地缓存作为 L1、Redis 作为 L2、数据库作为权威数据源。读取时按照本地缓存、Redis、数据库的顺序逐级查询,从而减少网络请求、降低 Redis 和数据库压力;更新时先提交数据库,再删除 Redis,并通知所有应用实例使本地缓存失效。它适合访问频率高、更新频率低且能够容忍短暂不一致的数据,但必须处理多实例一致性、缓存击穿、容量限制和失效消息丢失等问题。
参考资料
3. 缓存数据的原子性更新如何保证?举例说明
什么是原子性更新?
原子性更新是指一个操作或一组操作在执行过程中不可被其他客户端命令插入。其他客户端只能看到操作执行前或执行后的状态,不能看到中间状态。
它主要用来解决并发环境下的竞态问题,例如:
- 多个请求同时修改库存,造成库存超卖;
- 使用“先查询、再修改”的方式更新计数,造成数据丢失;
- 多个有关联的缓存数据只更新了一部分,产生逻辑上的不一致。
需要注意:Redis 中的原子执行不等于事务回滚,也不代表 Redis 与数据库之间能够自动保持原子性。
1. 优先使用 Redis 原子命令
如果一个操作可以通过 Redis 的单条命令完成,应优先使用原子命令,例如:
INCR、DECR:原子增减计数;HINCRBY:原子修改 Hash 字段;ZINCRBY:原子修改有序集合分数;SET key value NX EX seconds:仅在 Key 不存在时写入并设置过期时间。
例如,对访问次数进行加一:
INCR article:1001:view_count
Redis 的命令执行具有原子性,因此不会出现多个请求同时读取旧值、分别加一,最终造成计数丢失的问题。
2. 使用 MULTI/EXEC 事务
当多个命令需要连续执行,且执行过程中不能插入其他客户端的命令时,可以使用 MULTI/EXEC:
MULTI
SET user:{1001}:name "程序厨"
SET user:{1001}:version 2
EXEC
MULTI 之后的命令会先进入队列,执行 EXEC 后,Redis 会按顺序执行这些命令。在事务执行期间,不会插入其他客户端的命令。
但 Redis 事务与关系型数据库事务不同:
- Redis 事务不支持回滚;
- 如果命令在入队时就存在语法错误,整个事务通常不会执行;
- 如果某条命令在
EXEC阶段发生运行时错误,其他命令仍然会继续执行; - 因此不能简单理解为“所有命令要么全部成功,要么全部失败”。
例如:
SET counter "abc"
MULTI
INCR counter
SET status "done"
EXEC
由于 counter 不是整数,INCR 会执行失败,但后面的 SET status "done" 仍然可能执行成功。
因此,MULTI/EXEC 主要保证的是命令连续执行、不被其他客户端打断,而不是失败后自动回滚。
3. 使用 WATCH 实现乐观锁
如果业务需要“先读取、判断,再更新”,仅使用 MULTI/EXEC 还不够,可以配合 WATCH 实现乐观锁。
例如扣减库存:
WATCH stock:{1001}
GET stock:{1001}
# 客户端判断库存是否充足
MULTI
DECRBY stock:{1001} 1
SADD orders:{1001} order:9001
EXEC
WATCH 会监控指定 Key。如果从执行 WATCH 到执行 EXEC 期间,该 Key 被其他客户端修改,EXEC 会返回空结果,事务中的命令不会执行,客户端需要重新读取数据并重试。
这种方式适合并发冲突较少的场景。并发冲突较高时,事务可能频繁失败和重试,此时通常更适合使用 Lua 脚本。
4. 使用 Lua 脚本完成条件判断和多步更新
对于“判断条件成立后,再执行多个更新”的场景,可以将逻辑封装成 Lua 脚本。Redis 执行 Lua 脚本期间,不会执行其他客户端的命令。
下面以防止库存超卖为例,同时使用订单号保证接口幂等:
-- KEYS[1]:库存 Key
-- KEYS[2]:已处理订单集合
-- ARGV[1]:购买数量
-- ARGV[2]:订单号
local quantity = tonumber(ARGV[1])
if not quantity or quantity <= 0 then
return redis.error_reply("invalid quantity")
end
-- 相同订单已经处理过,避免重试时重复扣减
if redis.call("SISMEMBER", KEYS[2], ARGV[2]) == 1 then
return 2
end
local stock = tonumber(redis.call("GET", KEYS[1]) or "0")
if stock < quantity then
return 0
end
redis.call("DECRBY", KEYS[1], quantity)
redis.call("SADD", KEYS[2], ARGV[2])
return 1
调用方式:
EVAL "<脚本内容>" 2 stock:{1001} orders:{1001} 1 order:9001
返回值可以约定为:
0:库存不足;1:扣减成功;2:该订单已经处理过。
Lua 脚本的优点是条件判断和数据修改都在 Redis 服务端完成,既能保证执行期间不被其他命令插入,也减少了客户端与 Redis 之间的网络往返。
但需要注意:
- Lua 脚本发生运行时错误时,Redis不会回滚脚本中已经完成的写操作;
- 因此应在写操作之前完成参数、数据类型和业务条件检查;
- 脚本执行期间会阻塞 Redis 处理其他命令,所以脚本应尽量短小,不能执行耗时计算;
- 对可能重试的请求,应通过订单号、请求号等业务标识保证幂等性。
Redis Cluster 下的限制
在 Redis Cluster 中,Lua 脚本、事务以及其他多 Key 操作要求相关 Key 位于同一个哈希槽。
可以使用 Hash Tag 保证 Key 被分配到同一个槽:
stock:{1001}
orders:{1001}
两个 Key 中都包含 {1001},因此会被映射到同一个哈希槽。
与数据库一致性的边界
MULTI/EXEC、WATCH 和 Lua 脚本只能保证 Redis 内部操作的原子性,无法同时保证数据库操作和 Redis 操作的原子性。
如果一次业务操作既要修改数据库,又要更新缓存,通常需要结合以下方案:
- Cache Aside:先更新数据库,再删除缓存;
- 消息队列或事务消息;
- Outbox 本地消息表;
- 订阅数据库变更日志,通过 CDC 异步更新或删除缓存;
- 失败重试、幂等控制和补偿机制。
总结
保证缓存原子性更新时,应按照以下顺序选择方案:
- 单个操作优先使用 Redis 原子命令;
- 多条命令需要连续执行时使用
MULTI/EXEC; - 需要先读取再判断更新时,使用
WATCH + MULTI/EXEC; - 需要完成复杂条件判断和多个数据修改时,使用 Lua 脚本;
- 无论使用事务还是 Lua,都要注意 Redis 不提供运行时错误后的自动回滚;
- 如果操作同时涉及数据库和 Redis,还需要通过消息、重试、幂等和补偿机制解决跨系统一致性问题。
参考:Redis Transactions、Redis Lua Scripting、Redis Cluster
4. 基于 Redis 缓存的读写性能监控指标有哪些?如何设置告警?
内存方面:
通过used_memory和maxmemory计算内存占比,反映容量压力。若超过80%需触发告警,避免数据逐出或OOM崩溃
内存碎片率mem_fragmentation_ratio(used_memory_rss/used_memory)超过1.5时需告警,可能需手动执行内存整理或重启实例
监控evicted_keys(驱逐Key数)和expired_keys(过期Key数),突增可能表明内存不足或缓存策略失效
驱逐: Redis 在内存达到预设上限(maxmemory)时,根据配置的淘汰策略自动删除部分 Key 以释放内存的操作
吞吐和命中率方面
实时监控吞吐率,结合业务基线设置动态阈值,若有毛刺或激增,则需要告警
命中率:若Redis处理100次请求,其中80次直接从缓存中获取数据(命中),20次需查询数据库(未命中),则命中率为80%
当命中率低于阈值时,需要告警,可能需调整TTL或预加载热点数据
关注每秒写入数,如果大于阈值,也需要进行告警,高写入负载可能导致主从延迟或持久化阻塞
响应时间方面
通过slowlog记录执行时间超阈值(如10ms)的命令,每分钟超10次慢查询需告警并优化命令逻辑
监控网络时延,流量突增可能引发客户端缓冲区堆积或复制中断
连接与并发指标
connected_clients超过预设阈值需告警,可能需优化连接池或扩容
blocked_clients持续存在时,排查阻塞命令(如BLPOP)或资源竞争问题
这个题目的优先级不高,但是需要了解,如果仅学习 redis 知识的同学,建议写一个简单的 demo,使用 C++来操作一下 redis,了解一下工程中的使用应用方法
5. 对于低频访问但不可丢失的缓存数据,淘汰策略如何定制?
什么是缓存淘汰策略?
缓存淘汰策略是指 Redis 内存达到 maxmemory 上限后,决定删除哪些 Key 来释放空间的规则。
它主要解决 Redis 内存有限、数据持续增长的问题,避免 Redis 占用过多内存。但“不可丢失”需要区分两种含义:
- 业务数据不能丢失:数据必须以数据库等持久化存储为准,不能只保存在缓存中;
- 缓存副本不能被淘汰:需要通过实例隔离、过期时间和淘汰策略尽量保护这些 Key。
低频数据在 LRU、LFU 策略下很容易成为淘汰对象,因此不能简单选择 allkeys-lru 或 allkeys-lfu。
1. 最可靠的方案:使用独立 Redis 实例
如果这类数据确实不能被淘汰,最好将它与普通缓存拆分到不同的 Redis 实例中:
- 普通缓存实例使用
allkeys-lru、allkeys-lfu等策略; - 重要数据实例使用
noeviction; - 为重要数据实例预留足够内存,并设置监控和告警。
重要数据实例可以配置为:
maxmemory 4gb
maxmemory-policy noeviction
noeviction 表示达到内存上限后不主动淘汰已有数据,而是让可能增加内存的写命令返回错误。
它可以防止已有 Key 因内存不足而被淘汰,但也存在明显代价:
- 内存满后,新增或扩容数据的写操作可能失败;
- 业务必须正确处理 Redis 返回的 OOM 错误;
- 必须提前做好容量评估、扩容和告警。
需要注意,将数据放到同一个 Redis 实例的不同逻辑数据库中,不能实现真正的内存和淘汰隔离,因为 maxmemory 和淘汰策略是实例级配置。
2. 无法拆分实例时,使用 volatile-* 策略隔离
如果普通缓存和重要数据必须存放在同一个 Redis 实例中,可以采用以下方式:
- 重要数据不设置过期时间;
- 普通缓存设置合理的 TTL;
- 使用
volatile-lru、volatile-lfu或volatile-ttl。
例如:
maxmemory 4gb
maxmemory-policy volatile-lru
写入重要数据时不设置 TTL:
SET important:config:1001 "value"
写入普通缓存时设置 TTL:
SET product:1001 "value" EX 3600
在 volatile-* 策略下,Redis 只会从设置了过期时间的 Key 中选择淘汰对象,没有 TTL 的重要 Key 不会参与内存淘汰。
常见选择包括:
volatile-lru:淘汰设置了 TTL 且最近最少访问的数据;volatile-lfu:淘汰设置了 TTL 且访问频率较低的数据;volatile-ttl:优先淘汰剩余过期时间较短的数据。
这种方案能够保护没有 TTL 的重要数据,但仍有以下风险:
- 当没有可淘汰的过期 Key 时,其行为类似
noeviction,后续写入可能失败; - 重要数据持续增长,仍然可能耗尽实例内存;
- 普通缓存和重要数据仍然会相互争抢内存资源。
因此,实例隔离通常比在同一个实例中混合存储更加可靠。
3. 不要仅依赖“设置较长 TTL”
给数据设置几个月甚至一年的 TTL,只能延缓数据自然过期,不能保证它不会被内存淘汰。
如果使用的是以下策略:
allkeys-lru
allkeys-lfu
allkeys-random
那么即使 Key 还有很长的 TTL,也仍然可能在内存不足时被淘汰。尤其是低频访问数据,在 allkeys-lru 和 allkeys-lfu 下更容易被选中。
因此,真正不希望被淘汰的数据不应仅依赖较长 TTL,而应采用独立实例或“无 TTL + volatile 策略”。
4. Redis 不支持为单个 Key 设置淘汰优先级
Redis 原生没有提供类似下面这样的 Key 级别淘汰优先级:
priority = high
priority = low
如果业务需要按优先级保护数据,通常采用以下方式实现:
- 将不同优先级的数据存放到不同 Redis 实例;
- 高优先级数据不设置 TTL,普通数据设置 TTL,并使用
volatile-*策略; - 由业务系统维护数据分级,主动删除或迁移低优先级数据;
- 将低频但需要长期保留的数据保存到数据库、磁盘或对象存储中。
5. 做好容量监控和提前扩容
Redis 开源版通常是在内存达到 maxmemory 后触发淘汰,并不会固定在内存使用率达到 80% 时自动开始淘汰。
可以在业务监控中自行设置阈值,例如内存使用率达到 70%~80% 时告警,重点监控:
used_memory
maxmemory
evicted_keys
expired_keys
keyspace_hits
keyspace_misses
其中:
evicted_keys持续增长,说明正在发生内存淘汰;used_memory接近maxmemory,说明需要清理数据或扩容;- 命中率持续下降,可能说明有价值的数据正在被淘汰。
对 noeviction 实例,应在达到内存上限之前完成扩容,否则写操作会直接失败。
6. “不可丢失”还需要持久化保障
淘汰策略只能控制 Redis 内存不足时是否删除 Key,不能防止进程崩溃、机器故障或错误操作造成的数据丢失。
如果数据在 Redis 中也需要一定的持久性,还应结合:
- AOF 持久化;
- RDB 快照;
- 主从复制和哨兵或集群;
- 定期备份及恢复演练。
即使开启了持久化和复制,Redis 也不应轻易作为关键业务数据的唯一数据源。真正不可丢失的数据应保存到具备可靠持久化能力的数据库中,Redis 主要保存它的加速副本。
总结
对于低频访问但不可丢失的数据,推荐方案是:
- 业务原始数据保存在数据库等持久化存储中;
- 将重要数据与普通缓存拆分到不同 Redis 实例;
- 重要数据实例使用
noeviction,同时做好容量评估和扩容; - 如果无法拆分实例,可以让重要 Key 不设置 TTL,普通 Key 设置 TTL,并使用
volatile-*策略; - 不要依赖较长 TTL,也不要使用
allkeys-lru或allkeys-lfu保护低频数据; - Redis 不支持为单个 Key直接设置淘汰优先级,需要通过实例隔离或 TTL 分组实现;
- 配合监控、持久化、复制和备份,防止故障导致数据丢失。
参考:Redis Key Eviction、Redis Persistence
6. 如何利用 Redis 缓存实现接口请求频率限制?
可以把同一限流对象的请求计数放到 Redis,让不同应用实例共同判断是否超过额度。关键不是“缓存请求结果”,而是共享限流状态,并原子地更新状态。否则三台应用各自允许三次,合起来就可能放行九次。
以下使用 Redis 7.0 及以上已有能力,数字是教学推演,没有执行服务实验。限流和后文的 Lua 示例应放在独立练习环境;如果教学键已存在,请更换前缀,不覆盖或清理已有数据。
先明确限制谁、限制什么
例如,同一登录用户访问下单接口,每个窗口最多放行三次。键可以设计为 demo:repair:rate:{u42}:orders,同时包含用户和接口维度。限流身份应来自可信认证信息,不能任由请求参数伪造。按 IP 限流实现简单,但共享出口的用户可能相互影响,代理场景也需要正确识别来源。
本例采用首次请求开始的固定计数窗口:第一次请求创建计数键,有效期十秒;后续请求只增加计数,不延长有效期。这不是按自然整点切分的窗口,也不是“最近任意十秒”的滑动窗口。键过期后,下一个请求开启新窗口。
| 相对时刻 | 本窗口尝试计数 | 是否放行 |
|---|---|---|
| 0 秒,首次请求 | 1 | 是 |
| 1 秒 | 2 | 是 |
| 2 秒 | 3 | 是 |
| 3 秒 | 4 | 否 |
| 原窗口到期后,10.1 秒的新请求 | 1,新窗口 | 是 |
拒绝的请求也计入本例的尝试次数,不能减回三。真实过期处理不是应用侧精确定时回调;这里用时刻帮助理解有效期。Redis INCR 的限流示例
将计数与首次过期设置放在同一个脚本
单独调用 INCR 后再调用 EXPIRE,客户端可能在两次调用之间崩溃,留下没有过期时间的计数键;每次都重新设置十秒,又会变成持续延后窗口结束。因此,应在 Lua 中完成递增、首次设置过期和允许判断。
下面是完整教学脚本,可另存为 demo_rate_limit.lua。参数分别是额度和窗口毫秒数;结果依次为尝试计数、允许标记、剩余毫秒。教学键只存规范十进制非负整数(0或不含前导零的正整数),并由本协议维护。
-- KEYS[1]: isolated counter key; ARGV: limit, window milliseconds.
if #KEYS ~= 1 or #ARGV ~= 2 then
return redis.error_reply("Expected one key and two arguments")
end
local function positive_integer(value, maximum)
if not value or not string.match(value, "^%d+$") then
return nil
end
local number = tonumber(value)
if not number or number < 1 or number > maximum then
return nil
end
return number
end
local limit = positive_integer(ARGV[1], 1000000000)
local window_ms = positive_integer(ARGV[2], 604800000)
if not limit or not window_ms then
return redis.error_reply("Invalid limit or window")
end
local previous = redis.call("GET", KEYS[1])
if previous then
local value = tonumber(previous)
if not string.match(previous, "^%d+$") or not value
or (#previous > 1 and string.sub(previous, 1, 1) == "0")
or value > 1000000000000 then
return redis.error_reply("Invalid counter state")
end
if redis.call("PTTL", KEYS[1]) < 0 then
return redis.error_reply("Existing counter must have an expiry")
end
end
local count = redis.call("INCR", KEYS[1])
if not previous then
redis.call("PEXPIRE", KEYS[1], window_ms)
end
local allowed = 0
if count <= limit then
allowed = 1
end
return {count, allowed, redis.call("PTTL", KEYS[1])}
调用形式如下,这里只展示命令,不实际执行:
redis-cli --eval demo_rate_limit.lua 'demo:repair:rate:{u42}:orders' , 3 10000
应用检查第二个返回值:1 才继续业务,0 则拒绝,例如返回 HTTP 429。剩余有效期可辅助形成重试提示,但不是业务执行时间的保证。该脚本只访问传入的一个键;在 Redis Cluster 中由支持集群路由的客户端发送到对应分片。若以后扩展到多键,必须另外处理同槽限制。
脚本执行期间其他客户端命令不能插入这组判断和修改,但脚本出错不等于已执行写入自动回滚。所以参数和已有状态在写入前检查,脚本也应短小,不放长循环。Redis Lua 执行语义

对照共享键、首次设置过期以及四次请求结果,理解计数窗口为何不能每次续期。
选型与故障不能只看正常路径
固定窗口在边界附近可能集中放行。例如旧窗口已在 0 秒开启,在 9.7、9.8 秒用掉剩余额度,新窗口在 10.1、10.2、10.3 秒又放行三次:短时间内仍可能出现五次请求。若要求限制最近一段时间,可考虑滑动窗口;若希望控制平均速率并允许明确的突发量,可考虑令牌桶。更严格的状态管理也会增加存储和计算成本。
Redis 不可用时,应按业务选择“拒绝优先”还是“可用优先”:前者保护下游但可能误拒正常用户,后者允许降级但不能再声称全局额度严格有效。本地限流可做兜底,不等同共享计数。
客户端超时也不能直接认定脚本没有执行;无条件重试可能再次计数。生产实现应明确重复请求是否重复计数、故障切换导致状态丢失时如何降级,以及限流与下单幂等怎样分别处理。拿到额度不代表订单已经成功,限流不会代替业务事务。
面试回答
可以按用户、IP或接口在 Redis 保存共享限流状态,让多实例使用同一额度。固定窗口可用 INCR 计数,并在首次创建时设置过期;用短 Lua 脚本把递增、过期和允许判断原子执行,避免计数键无期限或窗口被不断续期。超额由应用拒绝。本例拒绝请求也计数,且固定窗口存在边界突发;更严格的时间语义可采用滑动窗口或令牌桶。还要考虑超时重试、Redis故障与降级,不能把限流成功当作业务成功。
7. 主从复制
主从复制原理
主从复制模式,主库和从库的数据一致,采取的是读写分离的方式,进而来降低Redis 服务器的压力
读操作:主和从都可
写操作:先写主,然后主同步给从(防止数据不一致)
主从复制过程

建立链接,使用RDB快照进行同步。
但是我们思考一下,同步过程中,主服务器继续执行写操作,那么从服务器则会漏掉这部分操作,所以主服务器在从服务器的同步过程中,会将新的写操作放到缓冲区里,从库同步完毕后,再执行缓冲区的文件,实现同步。
另外为了缓解主库同步压力,我们采取主-从-从的模式
如果主库和从库之间网络断开,则会导致不一致,主库这时会将其中断期间执行的操作,写入缓冲区,然后从库苏醒之后,会执行缓冲区的语句完成同步,不过缓冲区为环形数据结构,如果从库写入较慢,同样会有覆盖的风险,所以我们可以将该数据结构设置的大一些。
主从同步和故障切换存在哪些坑
主从同步的坑
数据不一致问题
全量同步过期健处理:全量同步过程中,主库的数据会完整地复制到从库。然而,如果主库上有一些键在同步过程中过期,但从库仍然会接收并存储这些即将过期的数据。
这可能导致主从库在一段时间内对于这些键的数据状态不一致。
例如,主库已经删除了某个过期键,但从库在接收全量数据时可能还会把这个过期键的数据保存下来,直到下一次同步更新或者自己检测到过期。
部分同步的偏移量错误:部分同步依赖于,主从库的复制偏移量,来确定需要同步的数据范围。如果网络抖动,或者其他原因导致偏移量记录错误,从库可能无法准确地获取需要同步的数据部分。
从而产生数据不一致。比如,由于网络短暂中断,从库记录的偏移量没有及时更新,恢复后就会错过一部分主库更新的数据。
同步延迟问题
网络带宽和性能影响:主从库之间的网络带宽是影响同步速度的关键因素之一。如果网络带宽不足,尤其是在主库数据更新频繁的情况下,从库同步数据会出现明显的延迟。例如,在一个高并发的电商系统中,主库每秒都有大量的订单状态更新,而主从库之间的网络带宽有限,从库就很难及时跟上主库的更新速度,导致数据延迟。
从库负载过高导致同步滞后:从库自身的负载情况也会影响同步效率。如果从库同时还承担了大量的读操作,其资源会被读操作占用,导致同步线程无法及时处理主库传来的数据,从而产生同步延迟。比如,一个从库既要处理大量的缓存读取请求,又要进行主从同步,就容易出现同步滞后的情况。
配置错误问题
复制参数设置不当:Redis 主从同步有许多配置参数,如复制积压缓冲区大小。如果这个参数设置过小,当主库产生的数据更新量超过缓冲区大小时,部分同步可能无法正常进行,只能进行全量同步,这会消耗大量的资源和时间。
主从库版本兼容性问题:如果主从库的 Redis 版本不一致,可能会出现一些不兼容的情况,影响主从同步
故障切换的坑
选举过程中的问题
脑裂现象:在故障切换过程中,如果网络分区等原因,导致部分从库无法正确感知主库的故障,可能会出现多个节点同时认为自己是主库的情况,这就是脑裂现象。
选举算法的公平性和效率问题:在有多个从库的情况下,选举新主库的算法如果设计不合理,可能会导致选举时间过长或者选出的新主库性能不佳。
例如,简单的基于从库优先级的选举算法,如果优先级设置不合理,可能会使性能较差的从库被选举为新主库,影响系统的整体性能。
8. 说一下 Redis 哨兵集群?
Redis 哨兵集群是 Redis 的一种高可用部署方案,通过多个哨兵节点监控主从节点的状态,实现自动故障转移和故障恢复。
哨兵集群通常由一个或多个Redis主节点和多个Redis从节点组成,以及至少三个哨兵节点。
主要功能包括:
哨兵节点会定期检查主从节点的健康状态,包括主节点是否存活、从节点是否同步主节点等。
当主节点宕机或不可用时,哨兵节点会选举一个从节点作为新的主节点,同时通知其他从节点切换到新的主节点。
当主节点重新上线时,哨兵节点会将其重新加入集群,并将之前选举的从节点切换回原来的从节点。
哨兵节点可以动态调整主从节点的配置,如调整节点权重、设置节点优先级等。
哨兵集群的优点包括:
哨兵集群能够自动检测主从节点的状态,并在发生故障时进行自动故障转移,保证服务的高可用性。
哨兵集群能够自动管理主从节点的切换和恢复,减少了运维的工作量。
哨兵集群支持动态添加和移除节点,可以根据业务需求灵活调整集群规模。
哨兵集群也存在一些缺点,如对网络延迟敏感、性能损耗等。
9. 说一下 Redis Cluster
Redis 提供的分布式数据库方案,集群通过分片的来进行数据共享,并提供复制和故障转移功能。
一个Redis 集群通常由多个节点组成,下图为集群的创建过程。

集群的数据结构,clusterNode 结构保存了一个节点的当前状态,会保存自己的状态,还会保存其他节点的状态。

槽指派
Redis 集群通过分片的方式来保存数据库中的键值对,如果有槽没有得到处理,那么集群将处于下线状态。
集群中会通过slots数组进行来判断节点是否处理槽 i ,节点之间会告知其他节点自己负责什么槽。
在集群中执行命令
如果键所在的槽并正好指派给了当前节点,那么节点立刻执行该命令
如果键所在的槽没有指派给当前节点,那么节点会像客户端发送一个
MOVED错误,指引客户端转向至正确节点。
Redis共有 16384个哈希槽,每个key通过CRC16的校验和对16384取模,来计算key在哪个槽。
ASK错误
这个错误多发生于重新分片的过程中,我们将某节点的槽迁移到另一节点时,此时客户端传来命令,如果没有在对应的节点内查询到该槽中值,则说明已经发生了迁移,此时则返回 ASK 错误,然后开始去目标节点进行查询。
Moven错误
这是正常请求错误,哈希槽已经被稳定地指派给了其他节点,而非当前请求节点
故障检测
每隔一段时间,主节点之间会发送ping信息,如果没有一定时间内,得到回应,那么则被认为是疑似下线,当半数节点认为其疑似下线时,则认定其为已下线。
故障转移步骤
复制下线主节点的所有从节点里面,会有一个节点被选中。
被选中的节点会执行SLAVEOF no one 命令,成为新的主节点,
将下线节点的所有槽指派下线,然后指向新的主节点。
向其他节点发送ping信息,告知别人自己变成了主节点。
10. 对比主从集群、哨兵集群和 Redis Cluster 的读写性能差异及优缺点
主从集群:读性能可以通过配置多个从节点来分担读压力,实现读操作的负载均衡,从而提高读性能。写性能则主要集中在主节点,所有写操作都要先到达主节点,然后再同步到从节点,在主节点性能瓶颈时可能影响写性能。
哨兵集群:本质上也是主从架构,读写性能方面与主从集群类似。不过,哨兵机制能在主节点出现故障时自动进行主从切换,保障系统的可用性,在故障切换期间可能会有短暂的读写中断。
Cluster 集群:采用数据分片的方式,数据分布在多个节点上,每个节点负责一部分数据的读写。理论上写性能可以随着节点的增加而线性扩展,读性能也能通过多个节点并行处理得到提升。但由于数据分布在多个节点上,在进行跨节点的复杂查询时,性能可能会受到一定影响。
优缺点对比
主从集群
优点:架构简单,易于理解和部署。主从数据同步机制较为成熟,能保证数据的一致性,通过增加从节点可轻松扩展读能力。
缺点:主节点是单点故障,如果主节点宕机,整个系统的写操作将无法进行,虽然可以手动切换从节点为主节点,但这需要人工干预,会导致一定时间的服务中断,并且随着从节点的扩展,也会增加主节点的同步压力
哨兵集群
优点:在主从集群的基础上,增加了自动故障转移功能。哨兵节点可以实时监控主从节点的状态,当主节点出现故障时,能自动选举新的主节点,提高了系统的可用性,减少了故障恢复时间。
缺点:架构相对复杂一些,需要额外的哨兵节点来进行监控和管理。同时,哨兵节点本身也存在单点故障的风险,虽然可以通过部署多个哨兵节点来解决,但会增加系统的复杂性和成本。
Cluster 集群
优点:具有良好的可扩展性,能够根据业务需求动态增加或减少节点,实现数据和负载的均衡分布。数据分布在多个节点上,提高了数据的可靠性和容错能力,单个节点故障只会影响到部分数据,不会导致整个系统不可用。
缺点:数据分布和管理较为复杂,需要考虑数据分片、数据迁移等问题。
11. 日常开发中如何评估 Redis 集群的最大承载流量?
应先把业务流量转换成具体 Redis 工作量,再用接近真实业务的负载逐步加压,找到满足延迟、错误率和资源约束的容量。历史流量和单机压测可以作为起点,但不能简单用单机峰值乘节点数作为集群保证。
本题所有数字都是假设算例,不是你的机器或 Redis 的固定性能;本轮没有执行压测。
先统一“流量”的单位与负载
业务 RPS 是每秒业务请求数,Redis 命令量是每秒执行命令数,两者不一定相同。例如每个请求执行两次 GET 和一次条件 Lua 调用,1,000 RPS 会带来约 3,000 次 Redis 顶层调用;Lua 内部的工作还要单独分析,不能把它当成与简单 GET 相同的成本。
还应说明读写比例、命令类型、Value 大小、Key 分布、TTL、连接数、Pipeline 深度、持久化和复制配置。小 Value 的 GET 峰值不能替代大 Value、复杂脚本或大量写入的容量。应把真实访问轨迹或相同分布的合成负载作为测试输入,避免一直命中同一个小键,却声称评估了全业务。
可复用的测试记录
每一档负载记录业务RPS、各主分片命令量、P50/P95/P99端到端延迟、错误/超时率、CPU、网络吞吐、内存及热点分布。SLOWLOG可定位部分命令执行耗时,但不包含完整客户端网络和排队体验。连接、客户端发压能力也可能先成为瓶颈。
从最先达到约束的分片推导容量
假设三个主分片 S1、S2、S3 在同类负载、相同延迟要求下,各能达到 10,000 次命令/秒。规划时每片预留 20% 余量,使用上限为:
每片计划上限 = 10000 × 0.8 = 8000 次命令/秒
均匀分布的总量 = 3 × 8000 = 24000 次命令/秒
如果命令分布变成 70%/20%/10%,总量为 Q 时,三个分片分别承担 0.7Q、0.2Q、0.1Q。因此:
Q <= min(8000 / 0.7, 8000 / 0.2, 8000 / 0.1)
= 11428.57... 次命令/秒
按整数不超过约束规划,可取约11428次命令/秒。
此时各片约为 8,000、2,286、1,143,后两片仍有余量,却不能自动接走同一个热点 Key 的命令。图中近似数已四舍五入,不要求相加正好等于整数总量。槽位数量均匀也不意味着访问流量均匀;一个热门键本身就可能集中在一片。
副本不是额外写入口,不能按“三主三副”直接算六倍写容量。读副本还要评估允许的复制延迟、读一致性和复制成本。

对照均匀与倾斜负载,理解为什么其他分片空闲时,集群仍可能先达到瓶颈。
用阶梯压测验证,而不是只找一个峰值
先固定数据和配置,预热到约定状态,再逐档增加负载。每档保持足够观察时间,重复测试并记录波动;逐档观察哪个资源、分片或延迟指标先越界。测试应明确发压方式:如果客户端必须等前一请求结束才发送下一请求,系统越慢,发出的负载也可能越低,不能据此忽略排队压力。
例如业务约定“P99不超过某个阈值、错误率低于某个阈值”,可用容量就是同时满足这些要求的负载区间,不是把CPU跑满时输出的最高命令量。Pipeline应接近业务实际使用方式;只用极深Pipeline得到的数字,不能当作单次请求的低延迟保证。Redis benchmark 的负载与Pipeline说明
最后还需独立测试突发、热点、大键、后台持久化、迁移和故障切换阶段。预留20%只是本例规划系数,不能证明任意故障都能承受。生产环境优先根据监控验证规划,不未经评估直接发压;容量结论应标明版本、拓扑、硬件、负载模型和测试日期。
面试回答
评估 Redis 集群容量,先区分业务RPS与Redis命令量,明确命令组合、数据大小、读写比例、访问分布、客户端及持久化配置。用真实或同分布负载阶梯压测,以P99延迟、错误率和各分片资源是否达标确定容量,再留余量并测试热点、突发和故障切换。集群受最先饱和的分片或公共资源限制,不能只把单机峰值乘节点数,也不能把副本当作额外写容量。历史流量可用于校准,但不替代新负载验证。
阅读导航
上一章:Redis 缓存
下一章:Redis 持久化




