Redis
1. 缓存: 降级限流保底策略
1.1 缓存穿透: 查无此人
解决方案
1.缓存空数据

2.布隆过滤器: 说有未必有, 言无必定无
实现方式有: Reddisson & Guava (数组越大误判率越小)

1.2 缓存击穿: HotKey已过期
解决方案
1.互斥锁: 强一致性 & 性能差
2.逻辑过期: 高可用 & 性能好
1.3 缓存雪崩: 大量key同时失效
解决方案
1.针对大量key同时失效: 给不同的key的TTL添加随机值
2.针对Redis服务宕机: 用哨兵模式 & 集群模式
3.设置多级缓存: Guava & Caffeine
1.4 双写一致: mysql与Redis的同步
这个问题必须要根据业务背景来分析!!! 依据需求来调整!
主要是分为一致性要求高 & 允许延迟一致
先删谁?
无论先删除缓存还是先修改数据库
都会导致缓存和数据库数据不一致的现象出现
1.4.1 强一致性: 读写锁
1.4.2 延时一致: 异步通知
基于消息队列MQ
基于Canal
1.5 持久化问题
1.5.1 RDB(Redis Database Backup) - 记快照
a. 主动备份
b. 被动备份 save time nums (在time内, 有至少nums个修改, 就启动RDB)
c.执行原理
bgsave的动机:Redis 要生成 RDB 快照,目标是在不长时间阻塞主线程处理请求的前提下,把“某一时刻的内存数据”落到磁盘。触发动作:主进程执行
bgsave后调用fork()创建子进程。fork阶段并不会把整份内存数据拷贝一遍,它主要做两件事:- 复制进程上下文(寄存器等)。
- 复制页表(page table),让子进程与主进程先共享同一批物理内存页。
- 核心机制:Copy-On-Write(COW)
- 读:主进程与子进程读同一份共享物理页,零拷贝。
- 写:主进程一旦要修改某个内存页,OS 触发 COW,把该页复制出一份“私有页”,主进程在私有页上写;子进程继续读旧页,保持快照视角不被污染。
图解:
1 | bgsave |
1.5.2 AOF(Append Only File) - 记过程
前因:RDB 属于“周期性快照”,在两次快照之间如果 Redis 崩溃,中间的数据可能整体丢失。
为了缩小这个数据丢失窗口,Redis 引入 AOF(Append Only File),通过记录写命令日志来提升数据安全性。开启方式:
在redis.conf中显式打开 AOF,并指定 AOF 文件名。
appendonly yes
appendfilename "appendonly.aof
含义是:Redis 开始把写命令追加记录到 AOF 文件,而不是只依赖 RDB 快照。核心可调点:AOF 何时刷盘(appendfsync)
通过appendfsync控制刷盘频率三种 appendfsync 策略的本质差异:always, everysec, no
appendfsync always
每执行一条写命令,就同步刷盘(fsync)。
结果:- 几乎不丢数据
- 磁盘 IO 极重,性能下降明显
- 只适合对数据安全极端敏感的场景
appendfsync everysec(默认)
写命令先进入 AOF 缓冲区,每秒刷盘一次。
结果:- 最多丢失 1 秒的数据
- 性能和安全性折中
- 这是绝大多数生产环境的选择
appendfsync no
写命令只写入缓冲区,什么时候落盘完全交给操作系统。
结果:- 性能最好
- 宕机时可能丢失大量数据
- 更像“写日志但不保证落盘”,可靠性最低
AOF重写:
由于AOF记录的是命令而不是最终数据, 记录过程中有对于同一个key的修改, 对于同一个key的历史命令是无效的且AOF会变得冗长, 因此AOF会进行重写: 用最少的一组命令,等价地表达当前内存中的数据状态。- 触发方式:
- 手动触发:执行
bgrewriteaof - 自动触发:Redis 在满足阈值条件时自动执行 AOF 重写
自动重写的触发条件(必须同时满足):- 当前 AOF 文件大小 相对上一次重写后的增长比例 达到阈值
- 当前 AOF 文件大小 超过最小体积限制
- 手动触发:执行
- 触发方式:
1.5.3 RDB & AOF 对比
| 对比维度 | RDB | AOF |
|---|---|---|
| 持久化方式 | 定期对整个内存做快照 | 记录每一次执行的写命令 |
| 数据完整性 | 不完整,两次快照之间的数据可能丢失 | 相对完整,取决于刷盘策略 |
| 文件大小 | 有压缩,文件体积小 | 记录命令,文件体积大(故rewrite) |
| 宕机恢复速度 | 快 | 慢 |
| 数据恢复优先级 | 低,数据完整性不如 AOF | 高,数据完整性更高 |
| 系统资源占用 | 高,fork + COW 消耗 CPU 和内存 | 平时较低,主要是磁盘 IO; rewrite 时 CPU 和内存开销大 |
| 使用场景 | 可容忍分钟级数据丢失,追求更快的启动速度 | 对数据安全性要求较高的场景 |
| 底层 | 二进制快照, 因此执行快 | RESP 协议格式 |
1.5.4 Q&A
Redis 默认不开启 AOF,一方面是因为 AOF 记录写命令历史,文件体积持续增长,不可避免会触发 AOF rewrite;rewrite 过程中 fork、COW、重写命令都会带来明显的 CPU、内存和磁盘 IO 开销。另一方面,大多数业务对数据安全性的要求并没有那么高,RDB 的周期性快照已经能够满足需求。
在对数据安全性要求较高的生产环境中: 通常会同时开启 RDB 和 AOF:RDB 用于提供快速冷启动能力,AOF 用于缩小运行过程中 Redis 宕机导致的数据丢失窗口。
Redis 重启时优先加载 AOF: 并不是因为 AOF 恢复更快,而是因为 AOF 记录的是写命令日志,描述的数据状态更新程度更高,更接近宕机前的真实内存状态。
1.6 数据删除策略
Redis 的数据删除并不是“到点立刻删”,而是基于性能与内存回收成本的权衡,采用多种策略组合完成。
1.6.1 过期键的判定基础 : 时间戳逻辑过期
Redis 为 key 设置过期时间后,会在内部维护一个 过期字典,记录 key 与其过期时间戳。
是否过期的判断逻辑是:
当前时间 ≥ 过期时间 → key 逻辑上已过期。
但是否“立刻从内存中删除”,取决于具体删除策略。
1.6.2 惰性删除 : 被动删除
惰性删除的思想是:用到检查过期否, 过期则删,不主动扫描。
- 当客户端访问某个 key 时:
- Redis 先检查 key 是否存在
- 再判断 key 是否已过期
- 若已过期,立即删除并返回不存在
特点:
- 优点:CPU 消耗极低,只在访问时才做检查
- 缺点:如果 key 一直不被访问,即使已过期,也会长期占用内存
惰性删除解决的是访问路径上的正确性问题,但不负责内存回收。
1.6.3 定期删除 : 主动扫描
定期删除的思想是:后台主动清理一部分过期 key。
Redis 会周期性地执行过期扫描任务:
- 从设置了过期时间的 key 中随机抽取一部分
- 删除其中已经过期的 key
- 控制单次执行时间,避免阻塞主线程
Redis 采用的是 渐进式清理,而不是一次性扫描全部 key。
- 特点:
- 优点:避免大量过期 key 长期占用内存
- 缺点:无法保证所有过期 key 都被立即删除
1.6.3.1 SLOW 模式(常规定期删除)
触发方式:
SLOW 模式由 Redis 的后台周期任务触发,是定期删除的默认运行模式。
执行特征:
- 执行频率:默认 10 次/秒(由
hz参数控制) - 每次执行:
- 从设置了过期时间的 key 中随机抽取一部分
- 删除其中已经过期的 key
- 单次执行时间受限,避免阻塞主线程
设计目的:
在系统负载正常时,稳定、持续地清理过期 key,避免内存逐步膨胀。
1.6.3.2 FAST 模式(加速定期删除)
触发条件:
当 Redis 判断过期 key 数量较多、清理压力偏大时,会进入 FAST 模式。
执行特征:
- 执行频率更高,但两次执行之间最小间隔约 2ms
- 单次执行时长更短(通常不超过 1ms)
- 清理行为更积极,但仍遵循时间片限制
设计目的:
在过期 key 堆积时,加快清理节奏,防止大量已过期 key 长期占用内存。
1.7 内存淘汰策略(Memory Eviction Policy)
内存淘汰策略是在 Redis 内存使用达到上限(maxmemory)时触发的机制,用于解决**内存不足时 Redis 如何“活下去”**的问题。
1.7.1 Redis 支持的内存淘汰策略分类
Redis 一共支持 8 种内存淘汰策略,可以按两个维度来理解:
淘汰范围
volatile-*:只从设置了过期时间的 key 中淘汰allkeys-*:从所有 key 中淘汰
淘汰算法
lru:最近最少使用lfu:最不经常使用random:随机淘汰ttl:剩余 TTL 最小
1.7.1.1 LRU 与 LFU 算法原理
LRU(Least Recently Used,最近使用最少)
LRU 的核心思想是:如果一个 key 最近一段时间没有被访问,那么它在未来被访问的概率也较低。
在 Redis 中,LRU 的判断依据是:
用当前时间减去 key 的最后一次访问时间
得到的时间差越大,说明该 key 越久未被访问
时间差越大,被淘汰的优先级越高
需要注意的是,Redis 实现的是近似 LRU,通过采样方式降低维护成本,而不是严格维护一个全局有序链表。
LFU(Least Frequently Used,使用频率最少)
LFU 的核心思想是:如果一个 key 被访问的频率较低,那么它对当前系统的重要性也较低。
在 Redis 中,LFU 的判断依据是:
- 为每个 key 维护一个访问频率计数
- 访问次数越少,说明该 key 使用频率越低
- 访问频率越低,被淘汰的优先级越高
为了避免“历史热点 key 永久不被淘汰”,Redis 的 LFU 引入了访问频率随时间衰减机制,使得算法能够反映近期的访问特征。
1.7.2 具体淘汰策略说明
1)noeviction(默认)
- 不淘汰任何 key
- 内存写满后,直接拒绝写请求
- 读请求仍然可以正常返回
2)volatile-ttl
- 只从设置了过期时间的 key中淘汰
- 优先删除 剩余 TTL 最小 的 key
3)volatile-random / allkeys-random
- 随机删除 key
- 无任何访问特征判断
特点:实现简单,但命中率不可控。
4)volatile-lru / allkeys-lru
- 基于 LRU(Least Recently Used)
- 优先删除最近最少被访问的 key
5)volatile-lfu / allkeys-lfu
- 基于 LFU(Least Frequently Used)
- 优先删除访问频率最低的 key
特点:
对突发流量更友好,能避免热点 key 被误删,是 LRU 的改进版本。
1.7.3 内存淘汰策略使用建议
- 默认优先
allkeys-lru
适合大多数存在冷热数据区分的业务场景。 - 访问频率差异不明显时使用
allkeys-random
数据访问均匀,随机淘汰即可。 - 有置顶 / 核心数据需求时使用
volatile-lru
核心数据不设 TTL,其余数据设 TTL,只淘汰可过期数据。 - 存在短时高频访问数据时使用 LFU 系列
可选allkeys-lfu或volatile-lfu。
2. 分布式锁
2.1 简介
2.1.1 why分布式锁
- 问题: 在单机里你用
synchronized或ReentrantLock就够了,因为大家在同一个 JVM 里。
一旦变成 分布式:N 台机器 N 个进程,锁就不再共享了。
多进程多机器并发访问同一份共享资源时,保证同一时刻只有一个执行者进入临界区
2.1.2 基本使用: setnx / lua脚本
2.1.3 Reddisson分布式锁的代码实现
锁的获取
可重入锁
主从一致性
问题: Redis节点宕机另起主节点导致的不同线程同时持有一把锁
解决: 红锁(性能差 /实现复杂 /运维繁琐)
拓展: CAP
2.2 WatchDog机制
2.3 面经
3. 集群
3.1 主从复制
一般是准备一个master节点, 准备多个slave节点,
将写操作交给master进行处理, 将读操作交给slave处理