Redis


1. 缓存: 降级限流保底策略

1.1 缓存穿透: 查无此人

Pasted image 20260125210409

解决方案
1.缓存空数据
Pasted image 20260125210445
Pasted image 20260125210525

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


1.2 缓存击穿: HotKey已过期

Pasted image 20260125214705

解决方案
1.互斥锁: 强一致性 & 性能差
Pasted image 20260125215007

2.逻辑过期: 高可用 & 性能好
Pasted image 20260125215513


1.3 缓存雪崩: 大量key同时失效

Pasted image 20260125221337

解决方案
1.针对大量key同时失效: 给不同的key的TTL添加随机值
2.针对Redis服务宕机: 用哨兵模式 & 集群模式
3.设置多级缓存: Guava & Caffeine


1.4 双写一致: mysql与Redis的同步

这个问题必须要根据业务背景来分析!!! 依据需求来调整!
主要是分为一致性要求高 & 允许延迟一致

先删谁?

无论先删除缓存还是先修改数据库
都会导致缓存和数据库数据不一致的现象出现
Pasted image 20260125224713

1.4.1 强一致性: 读写锁
Pasted image 20260125222843 Pasted image 20260125230523
1.4.2 延时一致: 异步通知

基于消息队列MQ
Pasted image 20260125230828

基于Canal
Pasted image 20260125231114

Pasted image 20260126092209

1.5 持久化问题

1.5.1 RDB(Redis Database Backup) - 记快照
Pasted image 20260126092925

a. 主动备份
Pasted image 20260126092955

b. 被动备份 save time nums (在time内, 有至少nums个修改, 就启动RDB)
Pasted image 20260126093031

c.执行原理

  • bgsave 的动机:Redis 要生成 RDB 快照,目标是在不长时间阻塞主线程处理请求的前提下,把“某一时刻的内存数据”落到磁盘。

  • 触发动作:主进程执行 bgsave 后调用 fork() 创建子进程。

  • fork 阶段并不会把整份内存数据拷贝一遍,它主要做两件事:

    1. 复制进程上下文(寄存器等)。
    2. 复制页表(page table),让子进程与主进程先共享同一批物理内存页。
    • 核心机制:Copy-On-Write(COW)
    • :主进程与子进程读同一份共享物理页,零拷贝。
    • :主进程一旦要修改某个内存页,OS 触发 COW,把该页复制出一份“私有页”,主进程在私有页上写;子进程继续读旧页,保持快照视角不被污染。

图解:
Pasted image 20260201110917

1
2
3
4
5
6
bgsave
-> fork(复制页表,共享物理内存页)
-> 子进程:读“快照视角”的内存 -> 写临时RDB -> 原子替换旧RDB
-> 主进程:继续处理请求
读:共享页
写:触发COW,复制被写的页(额外内存+抖动来源)
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 内存淘汰策略使用建议
  1. 默认优先 allkeys-lru
    适合大多数存在冷热数据区分的业务场景。
  2. 访问频率差异不明显时使用 allkeys-random
    数据访问均匀,随机淘汰即可。
  3. 有置顶 / 核心数据需求时使用 volatile-lru
    核心数据不设 TTL,其余数据设 TTL,只淘汰可过期数据
  4. 存在短时高频访问数据时使用 LFU 系列
    可选 allkeys-lfuvolatile-lfu

2. 分布式锁

Pasted image 20260208225222

2.1 简介

2.1.1 why分布式锁
  • 问题: 在单机里你用 synchronizedReentrantLock 就够了,因为大家在同一个 JVM 里。
    一旦变成 分布式:N 台机器 N 个进程,锁就不再共享了。

多进程多机器并发访问同一份共享资源时,保证同一时刻只有一个执行者进入临界区

2.1.2 基本使用: setnx / lua脚本
Pasted image 20260211233942
2.1.3 Reddisson分布式锁的代码实现
锁的获取
Pasted image 20260212091915
可重入锁
Pasted image 20260212235059
主从一致性

问题: Redis节点宕机另起主节点导致的不同线程同时持有一把锁
Pasted image 20260213001643

解决: 红锁(性能差 /实现复杂 /运维繁琐)
Pasted image 20260213002536
拓展: CAP

2.2 WatchDog机制

Pasted image 20260211234646

2.3 面经

Pasted image 20260213003824

3. 集群

3.1 主从复制

一般是准备一个master节点, 准备多个slave节点,
操作交给master进行处理, 将操作交给slave处理