第02章:AI写的缓存代码,数据一致性有保障吗?
第02章:AI写的缓存代码,数据一致性有保障吗?
用户更新了头像,但其他地方还显示旧头像。用户修改了价格,但商品页面还显示旧价格。
这是Redis缓存最常见的生产问题——数据库改了,缓存没跟上。
这一章告诉你:AI生成的缓存更新逻辑有哪些一致性漏洞,以及怎么用更好的提示词写出真正能保证数据一致性的缓存代码。
ℹ️ 版本说明:本章基于 Redis 8.0。讨论的一致性问题适用于所有Redis版本,代码示例用 Python 3.13 + redis-py 5.x。
2.1 AI默认会生成什么
你让AI写"更新用户信息"功能:
def update_user_profile(user_id: int, data: dict):
# 1. 更新数据库
user = db.query(User).filter(User.id == user_id).first()
for k, v in data.items():
setattr(user, k, v)
db.commit()
# 2. 更新缓存(AI 常见的写法)
updated_data = user.to_dict()
redis_client.set(
f"user:{user_id}",
json.dumps(updated_data),
ex=3600
)
这段代码看起来合理,实际上有并发下的数据不一致风险。
2.2 AI通常遗漏的3个坑
⚠️ 坑1:更新缓存而不是删除缓存(Write Race)
"更新DB → 更新缓存"的操作有两步,中间有时间窗口。在并发下:
时间线:
T1: 请求A更新DB(值变成"新数据")
T2: 请求B也更新DB(值变成"更新的数据")
T3: 请求B更新缓存(缓存 = "更新的数据")✓
T4: 请求A更新缓存(缓存 = "新数据")✗ 覆盖了更新的数据!
结果:数据库里是最新值,但缓存里是旧值,而且这个不一致会一直持续到缓存过期。
正确做法:更新DB后删除缓存(而不是更新缓存)。下次有请求来时,从DB重新加载到缓存。
⚠️ 坑2:先删缓存再更新DB(Double Write Race)
有些AI会生成"先删缓存,再更新DB"的代码,认为这样"更安全":
redis_client.delete(f"user:{user_id}") # 先删
db.update(...) # 再改DB
这同样有问题:
时间线:
T1: 请求A删除缓存
T2: 请求B查询,缓存没有,从DB读到旧值,写回缓存(缓存 = 旧值)
T3: 请求A更新DB(DB = 新值)
结果:DB是新值,缓存是旧值,持续到缓存过期
⚠️ 坑3:更新DB成功但删缓存失败(数据分歧)
"更新DB → 删缓存"这两步不是原子的。如果第一步成功,第二步失败(Redis 临时不可用):
- 数据库:新值
- 缓存:旧值(而且没有机制去清理它)
后果:用户更新了信息,但在缓存过期之前(可能是1小时),所有读请求都返回旧值。
2.3 更好的提示词
提示词 P01:生成一致性安全的缓存更新模式
使用时机:任何需要同时更新数据库和缓存的操作。
你是一个 Redis 8.0 缓存一致性专家。
我需要实现「更新数据」的功能,同时维护 Redis 缓存的一致性。
业务场景:[如:用户更新个人信息;管理员修改商品价格]
涉及的缓存 key:[如:user:{id};product:{id}]
对数据实时性的要求:[如:允许最多60秒延迟 / 要求秒级一致]
请帮我实现「延迟双删」模式(Delayed Double Delete),这是目前最实用的缓存一致性方案:
1. 第一次删除缓存(更新DB之前)
2. 更新数据库
3. 延迟短暂时间后,第二次删除缓存(防止T2时间窗口的旧值被写回)
要求:
- 第二次删除用异步任务执行(不阻塞主请求)
- 如果删除缓存失败,有重试机制(最多重试3次)
- 操作过程中的日志记录(便于排查问题)
同时给出:这个方案的一致性窗口是多长(最坏情况下脏数据能存活多久)
技术栈:Python 3.13,redis-py 5.x,Celery(异步任务)
提示词 P02:为写入密集型场景设计 Write-Through 缓存
使用时机:你的数据写入频率高,且读操作对实时性要求高(比如计数器、用户状态)。
你是一个 Redis 8.0 + MySQL 9.0 缓存架构专家。
我有一个写入密集的场景,需要保证缓存和数据库的强一致性:
[描述场景,如:用户余额扣减;库存数量变更;实时在线人数]
当前痛点:[如:余额更新后立刻读取,缓存和DB不一致导致显示错误]
请帮我设计 Write-Through 方案:
1. 写操作:先更新Redis,再(异步或同步)更新MySQL
2. 读操作:直接从Redis读,不再查MySQL
3. Redis宕机降级:Redis不可用时,如何降级到直接读写MySQL
4. Redis重启后的数据恢复:Redis数据丢失后如何从MySQL热身(Warm-up)
注意:这个方案对Redis的可用性要求更高,请给出对应的监控和告警建议。
提示词 P03:用 Redis Lua 脚本实现原子性缓存操作
使用时机:你需要多步缓存操作必须原子执行(不能被中间打断)。
你是一个 Redis 8.0 Lua 脚本专家。
我需要实现以下原子操作(这几步必须要么全成功,要么全失败):
[描述操作,如:
1. 检查 user:balance:{id} 是否 >= amount
2. 如果是,将余额减少 amount
3. 在 user:transactions:{id} 列表里追加一条交易记录
4. 更新 user:last_activity:{id} 的时间戳]
请用 Lua 脚本实现这个原子操作,要求:
1. 脚本必须是幂等的(重复执行不会产生错误结果)
2. 如果检查条件不满足,脚本应该返回明确的错误码(不是抛异常)
3. 给出 redis-py 5.x 调用这个 Lua 脚本的 Python 代码
4. 说明这个 Lua 脚本在 Redis Cluster 环境下的限制(多 key 操作的约束)
2.4 数据一致性验收清单
| 检查项 | 验证方法 | AI辅助 |
|---|---|---|
| 更新逻辑是"删缓存"不是"更新缓存" | 搜索代码:更新操作后跟着的是 delete 还是 set |
贴代码,问"这里缓存更新逻辑在并发下是否安全" |
| 删缓存失败有重试机制 | 检查异常处理:redis.delete 失败时是否有重试 | 贴代码,问"如果删缓存时Redis不可用,会怎样" |
| 高并发写入场景用了Lua或事务 | 检查是否有余额/库存等原子操作场景 | 贴代码,问"这里的余额扣减在并发下是否有竞争条件" |
| 有缓存一致性的集成测试 | 测试:并发更新同一条数据,读取结果是否正确 | 贴场景,问"如何写一个能发现缓存一致性问题的测试" |
2.5 本章小结
如果你只记一件事:
更新数据时,删缓存而不是更新缓存。这是最简单也最安全的缓存一致性策略。
"延迟双删"能进一步缩小不一致窗口,用 P01 生成完整实现。
→ 第03章:AI帮我设计了Session存储,安全吗?