cache::set不随db事务回滚,因其与数据库事务完全独立,tp 8.1.4的事务仅作用于pdo sql,而cache直接操作redis等外部存储,无事务上下文联动,回滚无法影响已生效的缓存写入。

Cache::set 不随 Db 事务回滚,是因为缓存操作和数据库事务属于完全独立的系统,没有内置联动机制。TP 8.1.4 的事务管理只作用于 PDO 连接上的 SQL 执行,而 Cache 是对 Redis、File、Memcached 等外部存储的直接写入,不参与 MySQL 或 PDO 的事务上下文。
缓存不是事务参与者
MySQL 事务只能保证 InnoDB 表中数据的一致性,它无法控制 PHP 进程外的缓存服务。当你调用 Cache::set('order_123', $data),TP 实际是:
- 向 Redis 发送 SET 命令(或写入本地文件)
- 这个操作立即生效,不经过任何事务代理
- 即使后续 Db::transaction() 回滚,Redis 里那条 key 依然存在
常见误用场景
以下写法看似“原子”,实则不可靠:
- 先写缓存再写库:缓存已设,DB 插入失败 → 缓存脏数据
- 先写库再写缓存:DB 成功提交,缓存写失败 → 缓存缺失,但 DB 有数据
- 在事务闭包内调用 Cache::set():事务回滚不影响缓存,且 TP 不做任何拦截或延迟
可靠替代方案
要让缓存状态与 DB 一致,需主动设计补偿逻辑,不能依赖自动回滚:
- 延迟写缓存:DB 提交成功后再 set;失败则不写
- 缓存+DB 双删策略:更新前先 Cache::delete('key'),再更新 DB,避免旧值残留
- 使用逻辑过期 + 异步重建:如车辆位置类场景,缓存值自带时间戳,过期后由首个请求异步回源,不阻塞也不强一致
- 事务后钩子(手动触发):在 Db::transaction() 外围捕获异常,显式调用 Cache::delete() 清理可能已写入的缓存
特别注意 TP 8.1.4 的行为细节
TP 默认的 Cache 驱动(如 file、redis)均无事务感知能力。即使你用的是 Redis,也不能指望 multi/exec 包裹 Cache::set —— TP 的 Cache 类不提供该能力,也不推荐手动混用底层连接。
若真需强一致性,应将缓存操作移出业务主流程,改用消息队列(如 think-queue)投递清理任务,或在应用层加版本号/时间戳校验读取时的数据新鲜度。











