Redis原理和高可用
Redis原理和高可用
这篇是01-Redis基础.md之后的进阶篇。基础篇解决“Redis怎么用”,这一篇重点解决“Redis为什么这样工作,以及放进真实后端系统后怎么保证可用性、性能和一致性”。
Redis在生产环境里通常不是孤立存在的。它一边承接高频读写、缓存、限流、排行榜、地理位置粗筛等场景,另一边又要和PostgreSQL/MySQL、MQ、CDC、监控系统协同工作。
1 | |
flowchart LR
A["业务服务"] --> B["Redis"]
A --> C["PostgreSQL / MySQL"]
C --> D["WAL / Binlog"]
D --> E["CDC: Debezium"]
E --> F["Kafka / MQ"]
F --> G["缓存同步消费者"]
G --> B
B --> H["监控: slowlog / memory / latency"]
4. 底层原理、高可用架构与数据流转
4.1 持久化机制:数据不丢失的秘密
Redis主要以内存工作,但它不是只能“断电即丢”。Redis提供了RDB、AOF,以及二者结合的混合持久化方案。
先明确一个底线:持久化不是绝对不丢数据,而是在性能、恢复速度、磁盘成本和可接受丢失窗口之间做取舍。
| 方案 | 核心思想 | 恢复速度 | 数据丢失窗口 | 文件体积 | 适合场景 |
|---|---|---|---|---|---|
| RDB | 定期生成内存快照 | 快 | 取决于快照间隔 | 小 | 备份、灾难恢复、快速重启 |
| AOF | 记录每条写命令 | 较慢 | 取决于appendfsync策略 |
大,需要重写 | 更低数据丢失窗口 |
| 混合持久化 | AOF文件前半段是RDB,后半段是增量AOF | 较快 | 接近AOF | 较折中 | Redis 4.0+常用方案 |
4.2 RDB快照
RDB会在某个时间点把Redis内存中的数据生成一个紧凑的二进制快照文件,通常是dump.rdb。
触发机制
常见触发方式:
1 | |
区别:
| 命令 | 行为 | 风险 |
|---|---|---|
SAVE |
主线程同步保存快照 | 会阻塞Redis,不适合生产高峰 |
BGSAVE |
fork子进程后台保存快照 | 主线程短暂fork,子进程写磁盘 |
配置自动快照:
1 | |
含义大致是:
- 900秒内至少1次写入,触发快照;
- 300秒内至少10次写入,触发快照;
- 60秒内至少10000次写入,触发快照。
BGSAVE背后的fork与写时复制
BGSAVE通常会fork出一个子进程,由子进程把快照写到磁盘。fork之后,父进程继续处理请求。Linux通过Copy-On-Write减少复制成本:父子进程先共享内存页,当父进程修改某个页面时,内核才复制这个页面。
1 | |
这带来两个实践影响:
- 内存压力高时,
BGSAVE可能因为fork或COW开销导致延迟抖动; - 写入很密集时,COW会消耗额外内存,不能把机器内存打满后再指望快照稳定运行。
RDB优缺点
优点:
- 文件紧凑,适合备份;
- 重启加载速度通常比AOF快;
- 对主线程影响较小,主要压力在fork和磁盘写入;
- 适合做定期全量快照。
缺点:
- 两次快照之间的数据可能丢失;
- fork在大内存实例上可能产生明显延迟;
- 无法像AOF一样按每条写命令追踪变更。
4.3 AOF追加日志
AOF是Append Only File。它把每条写命令追加到日志文件中,Redis重启时重新执行这些命令恢复数据。
开启AOF:
1 | |
也可以在线开启:
1 | |
但在线修改后要记得写回配置文件或执行合适的配置重写,否则重启后配置可能丢失。
appendfsync写入策略
AOF最关键的配置是:
1 | |
三种策略:
| 策略 | 含义 | 性能 | 数据安全 |
|---|---|---|---|
always |
每次写命令都fsync | 最慢 | 最安全 |
everysec |
每秒fsync一次 | 折中 | 最多可能丢约1秒数据 |
no |
交给操作系统决定何时刷盘 | 最快 | 丢失窗口不可控 |
生产中常见选择是everysec。它不是绝对不丢数据,但在性能和可靠性之间比较均衡。
AOF重写
AOF会不断追加,文件会越来越大。例如:
1 | |
最终状态可能只是:
1 | |
AOF重写就是把“冗余历史命令”压缩成“恢复当前状态所需的最小命令集合”。
需要注意版本差异:Redis 7.0之后使用multi-part AOF机制,AOF不再只是一个单文件模型,而是由base file、incremental files和manifest共同管理。理解时仍可以按“基础状态 + 增量写入”这个模型来把握,运维备份时则要按实际版本的文件组织来处理。
手动触发:
1 | |
常见配置:
1 | |
含义是:当AOF文件比上次重写后增长到一定比例,并且超过最小体积后,自动触发重写。
AOF优缺点
优点:
- 数据丢失窗口更小;
- 追加写文件,符合磁盘顺序写特性;
- AOF文本协议可读性比RDB更好,排查时更直观。
缺点:
- 文件通常比RDB大;
- 重放AOF恢复速度可能慢于RDB;
always策略会显著影响写入性能;- 重写期间也有fork、COW和磁盘压力。
4.4 混合持久化
Redis 4.0之后支持AOF混合持久化,常见配置:
1 | |
它的思路是:
1 | |
好处:
- 恢复时先快速加载RDB部分;
- 再重放少量AOF增量;
- 兼顾RDB恢复速度和AOF较小丢失窗口。
实践建议:
- 只做纯缓存且允许丢失:可以关闭持久化,但要有缓存预热和降级方案;
- 做会话、排行榜、计数等较重要数据:优先考虑
AOF everysec或混合持久化; - 真正业务事实:不要只放Redis,最终仍应落到DB或其他可靠存储。
4.5 内存管理与淘汰策略
Redis是内存数据库,maxmemory是生产环境必须认真配置的参数。
1 | |
当Redis内存达到maxmemory后,会根据maxmemory-policy决定怎么处理新写入。
常见淘汰策略
| 策略 | 含义 | 适合场景 |
|---|---|---|
noeviction |
不淘汰,写入可能返回错误 | Redis存的是重要数据,不允许自动淘汰 |
allkeys-lru |
从所有key中淘汰最近最少使用的key | 通用缓存 |
volatile-lru |
只从设置了TTL的key中按LRU淘汰 | 混合数据,只希望淘汰缓存key |
allkeys-lfu |
从所有key中淘汰最不经常使用的key | 热点相对稳定的缓存 |
volatile-lfu |
只从设置了TTL的key中按LFU淘汰 | 设置TTL的热点缓存 |
allkeys-random |
从所有key中随机淘汰 | 访问分布接近随机 |
volatile-random |
从设置TTL的key中随机淘汰 | 简单TTL缓存 |
volatile-ttl |
优先淘汰剩余TTL短的key | 希望快过期的key先被淘汰 |
LRU与LFU怎么选
LRU看“最近有没有用过”,LFU看“过去一段时间用得频不频繁”。
1 | |
选择建议:
- 访问热点变化快:
allkeys-lru更灵活; - 热点长期稳定:
allkeys-lfu更能保住真正热Key; - 同一个实例里既有缓存又有不可淘汰数据:优先用
volatile-*,并确保缓存key设置TTL; - Redis只做缓存:
allkeys-lru或allkeys-lfu更直接; - 不允许Redis自行丢key:
noeviction,但业务必须处理写入失败。
注意:Redis的LRU/LFU是近似算法,不是每次都全量扫描所有key。这样做是为了在性能和淘汰效果之间取得平衡。
4.6 高可用架构演进
Redis高可用通常按下面路径演进:
1 | |
4.7 主从复制:Primary-Replica
传统资料常叫Master-Slave,现在更推荐叫Primary-Replica。
核心模式:
1 | |
配置从节点:
1 | |
或运行时执行:
1 | |
主从复制解决的问题:
- 主节点数据有副本;
- 读请求可以分摊到副本;
- 故障切换时有候选节点;
- 备份和部分分析读取可以从副本执行。
但它不等于完整高可用:
- 主节点宕机后,不会自动完成故障切换;
- 复制通常是异步的,主节点刚写完,从节点可能还没同步;
- 读从节点可能读到旧数据;
- 主节点宕机时,未复制到副本的数据可能丢失。
读写分离要特别注意“读己之写”。如果用户刚更新个人资料,下一次请求却读了副本,可能看到旧数据。解决思路包括:
- 写后短时间读主;
- 对关键读强制读主;
- 使用版本号或更新时间判断;
- 接受短暂最终一致,但前端和业务文案要有预期。
4.8 Sentinel哨兵模式
Sentinel用于监控Redis主从,并在主节点故障时自动选举新主节点。
典型结构:
1 | |
Sentinel主要职责:
- 监控primary和replica是否可达;
- 判断主节点是否下线;
- 多个Sentinel达成一致后执行故障转移;
- 从replica中选出新primary;
- 通知客户端新的主节点地址。
关键概念:
| 概念 | 含义 |
|---|---|
| 主观下线 | 单个Sentinel认为某节点不可达 |
| 客观下线 | 足够多Sentinel都认为主节点不可达 |
| quorum | 判断客观下线所需的票数 |
| failover | 选新主、重配从节点、更新拓扑 |
实践建议:
- Sentinel至少部署3个,避免单点和误判;
- Sentinel本身要分散在不同机器或故障域;
- 客户端要支持Sentinel发现,不要写死Redis主节点IP;
- Sentinel解决自动故障切换,不解决单机容量瓶颈;
- 故障转移期间可能有短暂不可写和数据丢失窗口。
4.9 Redis Cluster集群模式
Redis Cluster用于解决单机内存容量和单节点写入瓶颈。
它把整个key空间划分为16384个哈希槽:
1 | |
每个主节点负责一部分槽:
1 | |
flowchart LR
A["client"] --> B{"key -> hash slot"}
B --> C["node-1: slots 0-5460"]
B --> D["node-2: slots 5461-10922"]
B --> E["node-3: slots 10923-16383"]
C --> C1["replica"]
D --> D1["replica"]
E --> E1["replica"]
如果客户端访问了错误节点,Redis会返回重定向:
1 | |
成熟客户端会根据MOVED更新本地槽位缓存,并把请求发到正确节点。
Hash Tag与多key操作
Cluster中,多key命令要求key位于同一个slot。可以用Hash Tag控制槽位:
1 | |
Redis只计算{1001}这部分的hash,因此这些key会落在同一个slot。
Cluster适合什么
适合:
- 单机内存放不下;
- 单节点写吞吐不够;
- key天然可分片;
- 可以接受Cluster客户端、重定向和运维复杂度。
不适合:
- 大量跨key、跨slot事务;
- 依赖多个DB编号隔离业务;
- 运维能力不足但强行上集群;
- 数据模型还没稳定就急着分片。
Cluster的本质不是“更高级的单机Redis”,而是把数据分散到多个主节点上。它提升容量和吞吐,也带来槽位迁移、客户端路由、跨slot限制、故障恢复等复杂度。
4.10 异构数据同步:CDC流式架构
缓存一致性的一个难点是:业务代码更新数据库后,还要记得更新或删除缓存。随着服务变多,缓存逻辑很容易散落在各个代码路径中。
CDC,Change Data Capture,变更数据捕获,是另一种思路:数据库仍然是事实源,系统从数据库日志里捕获已提交变更,再异步同步到Redis。
4.11 Debezium监听PostgreSQL WAL同步Redis
PostgreSQL的WAL,Write-Ahead Logging,是数据库记录变更的日志。Debezium PostgreSQL connector可以基于逻辑复制捕获行级变更:首次连接时先做一致性快照,之后持续捕获已提交的INSERT、UPDATE、DELETE事件,并把变更事件写入Kafka topic。
典型链路:
sequenceDiagram
participant App as 业务服务
participant DB as PostgreSQL
participant WAL as WAL
participant D as Debezium
participant K as Kafka
participant C as Cache Consumer
participant R as Redis
App->>DB: INSERT / UPDATE / DELETE
DB->>WAL: commit后写入WAL
D->>WAL: logical decoding
D->>K: change event
C->>K: consume event
C->>R: DEL / SET / HSET
PostgreSQL侧常见前置条件:
1 | |
复制用户需要具备复制权限:
1 | |
Debezium connector配置形态大致如下,真实环境不要把密码写进配置仓库:
1 | |
缓存消费者根据事件操作Redis:
| DB事件 | Redis处理 | 说明 |
|---|---|---|
INSERT |
SET / HSET / DEL |
可以直接写缓存,也可以只删除相关缓存 |
UPDATE |
优先DEL |
避免字段计算不一致,下一次读再重建 |
DELETE |
DEL |
删除对象缓存和相关索引缓存 |
CDC同步Redis的优势
- 业务代码不必到处写缓存删除逻辑;
- DB提交成功后,变更事件由日志驱动,不依赖业务服务临时状态;
- 多个下游可以共享同一份变更流,比如Redis、搜索索引、审计系统;
- 可以把缓存同步从请求链路中移出,降低业务接口延迟。
CDC不是强一致魔法
CDC解决的是“变更最终能被看见”,不等于“Redis和DB每一毫秒都一致”。
风险点:
- CDC有延迟,Redis短时间可能还是旧值;
- Kafka或消费者积压会扩大不一致窗口;
- replication slot如果长期不消费,PostgreSQL WAL可能堆积;
- 消费者必须幂等,重复事件不能写坏缓存;
- 表结构变化会影响事件格式;
- 初始快照和增量切换期间要规划好缓存预热和读策略。
一个务实原则:CDC适合做最终一致性兜底和大规模解耦,不适合替代关键请求里的强一致校验。
5. 性能调优、实战避坑与一致性保障
5.1 缓存与数据库一致性:核心难点
缓存一致性不是一道单选题。它的难点来自三件事:
- DB和Redis是两个系统,无法天然共享一个本地事务;
- 请求是并发的,读写顺序可能交错;
- 网络、进程、MQ、CDC都有延迟和失败。
所以工程目标通常不是“永远强一致”,而是先分级:
| 数据类型 | 一致性目标 | 典型处理 |
|---|---|---|
| 余额、库存、权限判定 | 强一致或接近强一致 | DB事务、唯一约束、锁、版本号 |
| 用户资料、商品详情 | 短暂最终一致 | Cache Aside、删除缓存、MQ重试 |
| 排行榜、计数、热度 | 最终一致 | 异步汇总、定期校正 |
| 推荐、浏览量、UV | 可容忍误差 | 批量同步、降级、离线修正 |
5.2 经典缓存更新模式
Cache Aside Pattern:旁路缓存
这是最常见、最实用的模式。应用程序同时访问缓存和数据库。
读流程:
flowchart TD
A["读请求"] --> B{"Redis命中?"}
B -- "yes" --> C["返回缓存"]
B -- "no" --> D["查询DB"]
D --> E["写入Redis并设置TTL"]
E --> F["返回结果"]
写流程:
1 | |
优点:
- 简单,应用可控;
- Redis故障时可降级读DB;
- 不强迫所有数据都进缓存;
- 适合大多数后端服务。
缺点:
- 缓存逻辑在业务代码里;
- 并发读写下仍有短暂不一致;
- 需要处理缓存穿透、击穿、雪崩。
Read Through / Write Through
Read Through和Write Through里,应用程序理论上只和缓存层交互,由缓存代理负责读写DB。
1 | |
优点:
- 应用代码更干净;
- 缓存和DB读写策略集中管理。
缺点:
- 需要专门缓存代理或框架支持;
- 代理成为复杂组件;
- Redis本身不是关系型DB代理,很多时候需要自己实现。
适合:统一缓存平台、规范化强的业务中台、读写路径可控的系统。
Write Behind:异步缓存写入
Write Behind也叫Write Back。应用先写缓存,缓存层再异步写DB。
1 | |
优点:
- 写入延迟极低;
- 能批量落库,提高吞吐。
缺点:
- Redis或异步队列故障可能导致数据丢失;
- 数据恢复和重放复杂;
- DB不再是实时事实源;
- 对审计、事务和幂等要求很高。
适合:日志、计数、行为轨迹这类可补偿、可重放、可接受延迟的数据。不适合订单支付、库存扣减、账户余额等核心事实。
5.3 淘汰缓存 vs 更新缓存
写DB后,缓存到底应该“更新”还是“删除”?
很多大型团队更偏向删除缓存,而不是直接更新缓存。
原因不是“更新缓存做不到”,而是并发场景里更新缓存很容易发生时序错乱。
假设两个请求同时更新同一个商品:
sequenceDiagram
participant A as 请求A
participant B as 请求B
participant DB as DB
participant R as Redis
A->>DB: 更新价格=100
B->>DB: 更新价格=120
B->>R: SET price=120
A->>R: SET price=100
最终DB里是120,Redis里却可能是旧值100。
如果改成删除缓存:
1 | |
删除的好处是把复杂的“计算新缓存值”推迟到读路径,并让缓存从DB这个事实源重新构建。即使多删几次,最多导致缓存未命中;而乱更新一次,可能留下长期脏数据。
5.4 先删缓存还是先更新DB
方案一:先删缓存,再更新DB
1 | |
并发问题:
sequenceDiagram
participant A as 写请求A
participant B as 读请求B
participant DB as DB
participant R as Redis
A->>R: DEL cache
B->>R: GET cache miss
B->>DB: SELECT old value
B->>R: SET old value
A->>DB: UPDATE new value
最终DB是新值,但Redis可能被读请求写回旧值。
方案二:先更新DB,再删缓存
1 | |
这是Cache Aside里更常见的基线方案。
它也不是绝对无风险。极端并发下可能出现:
sequenceDiagram
participant B as 读请求B
participant A as 写请求A
participant DB as DB
participant R as Redis
B->>R: GET cache miss
B->>DB: SELECT old value
A->>DB: UPDATE new value
A->>R: DEL cache
B->>R: SET old value
这个窗口通常比“先删缓存再更新DB”小,因为读请求必须刚好在缓存未命中、读到旧DB值、并且写缓存晚于删除操作。但它仍然存在。
实践建议:
- 默认采用“更新DB后删除缓存”;
- 删除缓存失败要有重试;
- 对热点Key可配合互斥锁或逻辑过期;
- 对关键数据不要只靠缓存一致性,DB约束和事务仍是事实源。
5.5 延迟双删策略
用户给出的经典流程是:
1 | |
它的目的,是清理并发读请求在第一次删除后、DB更新前写回的旧缓存。
sequenceDiagram
participant A as 写请求A
participant B as 读请求B
participant DB as DB
participant R as Redis
A->>R: DEL cache
B->>R: GET miss
B->>DB: SELECT old value
B->>R: SET old value
A->>DB: UPDATE new value
A->>A: sleep N ms
A->>R: DEL cache again
N怎么估算?
1 | |
可以参考:
- DB查询P99耗时;
- Redis写入P99耗时;
- 服务线程调度和网络抖动;
- 主从复制延迟;
- 业务请求超时时间。
如果读DB P99是80ms,Redis写入P99是10ms,请求链路偶发调度20ms,那么N可以先从100ms ~ 300ms级别试验,而不是拍脑袋写固定1秒。关键是结合监控看效果。
注意:
- 延迟双删不是强一致方案;
- 在请求线程里
sleep会占用资源,生产中更适合投递延迟任务; N太短没效果,太长会延长不一致窗口或增加无意义删除;- 如果缓存重建非常频繁,双删会放大缓存未命中压力。
5.6 强一致性方案:锁与串行化
如果业务确实要求强一致,可以用锁把同一个资源的读写串行化。
1 | |
Java生态里常见用Redisson实现Redis分布式锁或读写锁。它能减少自己手写SET NX PX和Lua释放锁的细节风险。
但锁不是免费方案:
- 会降低并发;
- 锁粒度设计不好会造成排队;
- 锁超时时间过短会误释放,过长会拖慢恢复;
- 分布式锁不能替代DB唯一约束、事务和状态机;
- Redis故障时,锁服务本身也会影响业务。
适合加锁的场景:
- 同一商品库存扣减;
- 同一订单状态流转;
- 同一用户关键额度变更;
- 热点Key缓存重建。
不适合:
- 所有读请求都加锁;
- 对全局资源加大锁;
- 用锁掩盖不清晰的状态机设计。
5.7 最终一致性兜底:MQ与CDC
缓存删除失败是常见故障:
1 | |
如果没有兜底,旧缓存会一直存在到TTL过期。
MQ重试
可以把缓存删除消息投递到MQ:
flowchart TD
A["更新DB成功"] --> B["发送缓存删除消息"]
B --> C["MQ"]
C --> D["消费者DEL cache"]
D --> E{"成功?"}
E -- "yes" --> F["结束"]
E -- "no" --> G["重试 / 死信队列 / 告警"]
更稳的做法是Outbox模式:
1 | |
这样可以避免“DB更新成功,但消息还没发出去服务就崩了”的问题。
CDC兜底
CDC方案可以从DB日志里捕获变更:
1 | |
它适合作为最终一致性兜底:
- 业务代码漏删缓存,CDC还能捕获DB变更;
- 多服务共享同一张表时,不必每个服务都写缓存同步逻辑;
- 可以统一做缓存删除、搜索索引更新、审计事件。
但CDC也要监控延迟。如果消费者积压,Redis可能长时间落后于DB。
5.8 缓存穿透
缓存穿透指查询一个不存在的数据,缓存没有,DB也没有。如果恶意请求大量不存在ID,会绕过缓存直接打DB。
1 | |
对策:
| 方案 | 思路 | 注意事项 |
|---|---|---|
| 缓存空值 | DB查不到时缓存null短TTL |
防止空值过多,TTL要短 |
| 布隆过滤器 | 先判断ID是否可能存在 | 有误判,无漏判;需要维护过滤器 |
| 参数校验 | 非法ID直接拒绝 | 对恶意请求很有效 |
| 限流 | 对异常来源限速 | 防止被刷穿 |
布隆过滤器适合“存在集合相对稳定”的场景,比如商品ID、用户ID。如果数据频繁新增,要处理过滤器更新;如果需要删除,普通布隆过滤器不够,需要计数布隆过滤器或重建。
5.9 缓存击穿
缓存击穿指一个热点Key突然过期,大量请求同时发现缓存未命中,然后一起打到DB。
flowchart TD
A["热点Key过期"] --> B["大量请求miss"]
B --> C["同时查询DB"]
C --> D["DB压力飙升"]
对策:
互斥锁
只有一个请求重建缓存,其他请求等待或返回旧值。
1 | |
逻辑过期
缓存值里存一个逻辑过期时间,不让Redis自动删除热点Key。
1 | |
读到逻辑过期数据时:
- 先返回旧值,保证请求不打爆DB;
- 后台异步重建缓存;
- 适合热点商品、首页配置等允许短暂旧值的场景。
5.10 缓存雪崩
缓存雪崩指大量Key同时过期,或者Redis整体不可用,导致请求集中打到DB。
对策:
| 风险 | 对策 |
|---|---|
| 大量Key同一时间过期 | TTL加随机抖动 |
| Redis节点故障 | 主从、Sentinel、Cluster、多级缓存 |
| DB承接不了回源流量 | 限流、熔断、降级 |
| 冷启动缓存为空 | 缓存预热 |
| 热点缓存过期 | 逻辑过期、互斥重建 |
TTL随机抖动示例:
1 | |
雪崩治理的重点是“分散风险”,不要让大量请求在同一时刻做同一件昂贵的事。
5.11 Big Key排查与处理
Big Key是指value很大,或者集合成员很多的key。
典型例子:
1 | |
危害:
- 读写网络传输大;
- 删除时可能阻塞;
- RDB/AOF和复制压力变大;
- Cluster迁移槽位时耗时长;
- 客户端容易超时。
常用排查:
1 | |
查看单个key内存:
1 | |
渐进式扫描:
1 | |
处理方式:
- 大对象拆分成多个小key;
- 集合分页,不要一次取全量;
- List/ZSet加长度上限;
- 删除大Key优先使用
UNLINK,避免同步删除阻塞; - 对过期大Key做分批清理;
- 设计阶段就限制单key最大体积。
5.12 Hot Key排查与处理
Hot Key是访问量极高的key。它可能不大,但会让某个Redis节点CPU、网络或连接数飙升。
常见来源:
- 首页配置;
- 秒杀商品;
- 热门排行榜;
- 用户全局权限;
- 单个设备或航线状态被高频读取;
- 锁Key竞争激烈。
排查方法:
1 | |
注意:--hotkeys依赖Redis的LFU访问频率统计,实践中要确认当前版本和淘汰策略是否支持观察到有效频次。
也可以结合:
1 | |
处理方式:
- 本地缓存:应用内短TTL缓存热点配置;
- 多副本读:热点读分散到副本;
- Key分片:例如计数器拆成
counter:{id}:0..N; - 提前预热:活动开始前加载热点数据;
- 逻辑过期:热点Key不过期,由后台刷新;
- 限流降级:极端流量下返回降级结果。
5.13 Slowlog慢查询日志
Redis Slowlog记录执行时间超过阈值的命令。它记录的是命令执行耗时,不包含网络传输和排队时间。
常用配置:
1 | |
slowlog-log-slower-than 10000表示超过10000微秒,也就是10ms的命令会被记录。
查看慢查询:
1 | |
慢查询常见原因:
- 对大集合执行全量命令;
- Lua脚本执行过久;
- 删除大Key;
- 排序、交集、范围查询返回过多数据;
- 网络慢不一定进Slowlog,因为Slowlog不记录网络时间。
排查时不要只看Slowlog,也要结合:
1 | |
5.14 一套生产落地清单
上线Redis缓存或高可用架构前,可以按这张清单过一遍。
| 项目 | 检查点 |
|---|---|
| 容量 | 是否设置maxmemory,是否预估对象大小和Key数量 |
| 淘汰 | maxmemory-policy是否符合数据类型 |
| TTL | 缓存Key是否都有TTL,TTL是否有随机抖动 |
| 持久化 | RDB/AOF/混合持久化是否符合丢失窗口 |
| 高可用 | 单实例、Sentinel、Cluster选择是否匹配业务规模 |
| 一致性 | 是否明确Cache Aside、MQ重试或CDC兜底 |
| 热点 | 是否识别热点Key,是否有本地缓存或逻辑过期 |
| 大Key | 是否限制集合大小,是否有Big Key巡检 |
| 慢查询 | 是否配置Slowlog,是否接入监控 |
| 降级 | Redis不可用时业务如何处理 |
| 安全 | 是否限制网络暴露、认证、ACL和密钥管理 |
6. 面试与工程表达
如果面试或方案评审问“Redis高可用和一致性怎么设计”,不要只背概念,可以按下面结构回答:
1 | |
一个成熟回答的重点是:你能说清楚Redis解决了什么,也能说清楚它解决不了什么。