第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存储,安全吗?