Redis 不只问数据类型,更常问缓存穿透、缓存击穿、缓存雪崩、分布式锁和限流。本文用项目场景讲清这些问题是什么、为什么发生、怎么解决。

【一、为什么 Redis 高频问题很重要】

缓存用得好可以提升性能,用不好会把系统拖垮。

很多生产事故不是因为不会写 `GET` 和 `SET`,而是因为:

- 大量不存在的数据请求打到数据库。

- 热点 key 失效瞬间数据库被打爆。

- 大批缓存同时过期。

- 分布式锁没有释放导致业务阻塞。

- 限流没做导致接口被刷爆。

所以 Redis 高频问题非常适合写进项目和面试回答。

【二、缓存穿透】

缓存穿透指:请求的数据既不在 Redis,也不在 MySQL。

例如有人不断请求不存在的商品 id:

GET /products/999999999

每次流程都是:

查 Redis -> 没有

查 MySQL -> 也没有

返回不存在

大量这种请求会绕过缓存,直接打到数据库。

解决方案:

1. 缓存空值:数据库查不到,也把空结果缓存一小段时间。

2. 布隆过滤器:先判断 id 是否可能存在,不可能存在就直接拦截。

3. 参数校验:明显非法的 id 直接拒绝。

缓存空值示例:

python:

if user is None:

    redis.set(cache_key, "null", ex=60)

    return None

【三、缓存击穿】

缓存击穿指:某个热点 key 过期,大量请求同时打到数据库。

比如首页热门商品缓存过期的一瞬间,几千个请求同时过来。

解决方案:

1. 热点 key 设置较长过期时间,甚至逻辑过期。

2. 加互斥锁,只允许一个请求回源数据库。

3. 后台定时刷新热点缓存。

互斥锁思想:

发现缓存没有

-> 尝试获取锁

-> 获取锁的请求查数据库并重建缓存

-> 其他请求等待或返回旧值

【四、缓存雪崩】

缓存雪崩指:大量 key 在同一时间过期,导致请求同时打到数据库。

常见原因:

- 批量设置缓存时用了相同过期时间。

- Redis 故障。

- 热点缓存集中失效。

解决方案:

1. 过期时间加随机值。

2. 热点数据预热。

3. 多级缓存。

4. 限流和降级。

5. Redis 高可用部署。

示例:

python:

import random

expire_seconds = 3600 + random.randint(0, 300)

redis.set(cache_key, value, ex=expire_seconds)

【五、分布式锁是什么】

分布式锁用于多个服务实例之间控制同一资源的并发访问。

比如秒杀扣库存:

服务实例 A、B、C 同时处理同一个商品库存

如果不加控制,可能出现超卖

Redis 可以用 `SET key value NX EX seconds` 实现基础分布式锁。

SET lock:product:100 request_id NX EX 10

含义:

- `NX`:key 不存在时才设置成功。

- `EX 10`:10 秒后自动过期,防止死锁。

- `request_id`:锁的持有者标识,释放锁时要校验。

【六、释放锁为什么要校验 value】

不能简单地:

DEL lock:product:100

因为可能出现:

A 获取锁,业务执行太慢,锁过期

B 获取到新锁

A 执行完直接 DEL,把 B 的锁删了

所以释放锁要判断 value 是否是自己的。

真实项目建议使用成熟客户端或 Redisson 这类方案。Python 里可以结合 Lua 脚本保证判断和删除的原子性。

【七、Redis 限流怎么做】

限流用于防止接口被刷爆。

简单计数器限流:

每个用户每分钟最多请求 60 次

key = rate:user:1001:202606081930

流程:

请求进来 -> INCR key

第一次设置过期时间 60 秒

如果计数超过阈值 -> 拒绝

适合登录接口、短信验证码、AI 调用接口。

【八、常见坑】

- 缓存穿透只靠“查不到返回空”,但不缓存空值。

- 所有缓存过期时间完全一样。

- 分布式锁没有过期时间,业务异常后永远不释放。

- 释放锁不校验 value,误删别人的锁。

- 把 Redis 分布式锁当成绝对可靠,不考虑超时和重试。

- AI 接口不做限流,导致成本失控。

更多推荐