少女祈祷中...
NEKO
知识库

redis 数据过期淘汰面试要点

一、过期删除策略类

Q1:Redis 的 key 过期后是立即删除吗?

  • 不是。Redis 用惰性删除 + 定期删除组合,不会立即删。

  • 过期 key 在未被访问时可能仍占内存。

  • 所以会出现“key 已过期但内存没释放”的现象。

Q2:Redis 的过期删除策略有哪些?

  1. 惰性删除:访问 key 时才检查是否过期,过期就删并返回 nil。

  2. 定期删除:每隔一段时间随机抽查一批 key,删掉过期的。

  • 两者结合,兼顾 CPU 和内存。

Q3:惰性删除的优缺点?

  • 优点:CPU 友好,只在访问时检查。

  • 缺点:过期 key 不被访问就一直占内存,可能内存泄漏。

Q4:定期删除怎么工作?

  • 默认每秒 10 次(hz 控制)。

  • 每次随机抽取 20 个设置了过期时间的 key。

  • 删除其中已过期的。

  • 如果过期比例 > 25%,继续抽,直到低于阈值或超时。

  • 限制执行时间,避免阻塞主线程。

Q5:为什么定期删除是随机抽查,不是全量扫描?

  • 全量扫描会阻塞主线程,尤其大实例。

  • 随机抽查是 CPU 和内存的折中。

  • 保证过期 key 最终被清理,但不保证及时。

Q6:定期删除会阻塞 Redis 吗?

  • 会有一点。Redis 限制单次执行时间(默认 25ms)。

  • 超时就中断,下次继续。

  • 所以不会长时间阻塞。

Q7:从库会主动删除过期 key 吗?

  • 不会。从库等待主库的 DEL 命令。

  • 从库读到过期 key 时返回 nil(逻辑过期),但不物理删除。

  • 保证主从数据一致。

Q8:主库删除过期 key 后,从库怎么同步?

  • 主库删除时生成 DEL 命令写入 AOF 和 binlog。

  • 从库重放 DEL,删除对应 key。

  • 所以从库的删除由主库驱动。

二、内存淘汰策略类

Q9:Redis 有哪些内存淘汰策略?

共 8 种:

策略

范围

算法

noeviction

—

不淘汰,写入报错

allkeys-lru

所有 key

LRU

allkeys-lfu

所有 key

LFU

allkeys-random

所有 key

随机

volatile-lru

设了过期的 key

LRU

volatile-lfu

设了过期的 key

LFU

volatile-random

设了过期的 key

随机

volatile-ttl

设了过期的 key

优先淘汰 TTL 短的

Q10:怎么选择淘汰策略?

  • 纯缓存:allkeys-lru 或 allkeys-lfu。

  • 有持久数据 + 缓存:volatile-lru,只淘汰设了过期的。

  • 不能丢数据:noeviction,写入报错,需人工处理。

  • 访问频率差异大:lfu 更合适。

Q11:LRU 和 LFU 的区别?

  • LRU:淘汰最久未使用的,看“最近一次访问时间”。

  • LFU:淘汰访问频率最低的,看“访问次数”。

  • LRU 适合时效性强的场景,LFU 适合热点长期稳定的场景。

Q12:Redis 的 LRU 是精确的吗?

  • 不是。Redis 用近似 LRU。

  • 随机采样 N 个 key(maxmemory-samples,默认 5),淘汰其中最久未用的。

  • 采样越大越精确,但 CPU 开销大。

  • 每个 key 有 24 位 lru 时钟,记录访问时间。

Q13:Redis 的 LFU 怎么实现?

  • 每个 key 有 8 位计数器(logc)和 8 位时间戳。

  • 访问时概率性增加计数,避免短期热点。

  • 计数随时间衰减,防止老热点占坑。

  • 参数:lfu-log-factor、lfu-decay-time。

Q14:maxmemory 怎么设置?

  • maxmemory 设 Redis 可用最大内存。

  • 达到后触发淘汰策略。

  • 建议留出系统内存和 fork COW 余量。

  • 不设则可能 OOM。

Q15:淘汰什么时候触发?

  • 每次执行命令前检查内存。

  • 超过 maxmemory 就按策略淘汰。

  • 淘汰到低于限制才执行命令。

  • 如果 noeviction,直接返回错误。

三、过期与淘汰的关系类

Q16:过期删除和内存淘汰是一回事吗?

  • 不是。

  • 过期删除:key 到了 TTL 被删。

  • 内存淘汰:内存满了,按策略删 key。

  • 两者独立,但都影响内存。

Q17:key 没过期但内存满了会怎样?

  • 触发内存淘汰策略。

  • 如果策略是 volatile-* 且没有设过期的 key,可能无法淘汰,写入报错。

  • allkeys-* 则淘汰任意 key。

Q18:volatile-lru 和 allkeys-lru 怎么选?

  • 如果所有 key 都是缓存:allkeys-lru,淘汰范围大。

  • 如果部分 key 是持久数据:volatile-lru,只淘汰缓存。

  • 但 volatile-* 可能因可淘汰 key 不足而报错。

Q19:noeviction 适用什么场景?

  • 数据不能丢,且内存足够。

  • 内存满时写入报错,由应用处理。

  • 适合持久化数据为主的实例。

四、实战与排查类

Q20:怎么查看 key 的过期时间?

  • TTL key:剩余秒数,-1 永不过期,-2 不存在。

  • PTTL key:毫秒。

  • PERSIST key:移除过期时间。

Q21:怎么设置过期时间?

  • EXPIRE key seconds

  • PEXPIRE key milliseconds

  • EXPIREAT key timestamp

  • SET key value EX seconds

  • SETEX key seconds value

Q22:怎么查看内存使用?

  • INFO memory:used_memory、maxmemory、evicted_keys 等。

  • MEMORY USAGE key:单个 key 内存。

  • MEMORY STATS:内存分布。

  • redis-cli --bigkeys:找大 key。

Q23:怎么查看淘汰了多少 key?

  • INFO stats 的 evicted_keys。

  • 持续增长说明内存压力大。

  • 结合 maxmemory_policy 看策略。

Q24:大量 key 同时过期会怎样?

  • 定期删除压力大,可能卡顿。

  • 内存短时间不释放。

  • 解决:TTL 加随机值,打散过期时间。

Q25:怎么避免缓存雪崩式的集中过期?

  • 设置 TTL 时加随机偏移,如 3600 + random(0, 300)。

  • 热点 key 用逻辑过期,不设物理 TTL。

  • 多级缓存。

Q26:怎么找大 key?

  • redis-cli --bigkeys:扫描各类型最大 key。

  • MEMORY USAGE key:单 key 内存。

  • SCAN + STRLEN/LLEN/HLEN/SCARD/ZCARD 自定义脚本。

  • 大 key 要拆分或异步删除。

Q27:怎么异步删除大 key?

  • UNLINK key:后台线程删除。

  • FLUSHALL ASYNC、FLUSHDB ASYNC。

  • 避免 DEL 大 key 阻塞主线程。

五、高频陷阱题

Q28:key 过期了但内存没释放,为什么?

  • 惰性删除没访问到,定期删除还没抽到。

  • 所以内存延迟释放。

  • 可通过 SCAN 触发或等待定期删除。

Q29:TTL 返回 -1 和 -2 的区别?

  • -1:key 存在但没设过期时间。

  • -2:key 不存在或已过期。

Q30:从库读到过期 key 会返回什么?

  • 返回 nil(逻辑过期)。

  • 但不物理删除,等主库 DEL 同步。

  • 保证主从一致。

Q31:LRU 一定淘汰最久未用的吗?

  • 不一定。近似 LRU 采样淘汰。

  • 采样数越大越接近精确。

  • 默认 5,可能淘汰非最久未用的。

Q32:LFU 的计数器会一直增长吗?

  • 不会。8 位最大 255,且随时间衰减。

  • 概率性增加,避免短期热点刷高。

  • lfu-decay-time 控制衰减速度。

Q33:maxmemory 不设会怎样?

  • Redis 无限增长,直到 OOM 被系统杀。

  • 或触发 swap,性能骤降。

  • 生产必须设。

Q34:淘汰策略能动态改吗?

  • 能。CONFIG SET maxmemory-policy allkeys-lru。

  • 立即生效。

  • 也可改配置文件。

Q35:volatile-ttl 淘汰什么?

  • 在设了过期时间的 key 中,优先淘汰 TTL 最短的。

  • 适合希望快过期的先走的场景。

评论