本文目录导读:

在 Laravel 的官方文档和核心框架中,没有内置或强制要求使用“双删策略”(即先删除缓存、再更新数据库、再延迟删除缓存)来解决缓存与数据库的一致性问题。
这主要取决于以下几个原因和 Laravel 的默认设计哲学:
Laravel 的默认策略:Cache Invalidation(缓存失效)
Laravel 更倾向于使用 Cache Tagging(缓存标签) 或直接 删除(Forget) 来维护一致性。
-
原理: 在更新数据库数据后,立即删除对应的缓存,下次请求时,缓存找不到,自动从数据库拉取最新数据并重新缓存。
-
代码示例:
// 更新用户信息 public function update(Request $request, $id) { $user = User::find($id); $user->update($request->validated()); // 删除用户缓存 Cache::forget('user:'.$id); // 或者使用 Tags(Redis/Memcached 支持) // Cache::tags(['users'])->flush(); return response()->json($user); } -
适用场景: 大多数低并发、对秒级一致性要求不严格的 Web 应用,这种策略实现简单,且 Laravel 的 Eloquent 模型事件(如
saved,deleted)可以很方便地自动触发缓存清理。
双删策略的提出背景
双删策略主要为了解决高并发下“读后写” 导致的缓存与数据库短暂不一致问题(典型场景:一个读请求刚好拉取了旧数据并准备写回缓存,此时写请求删除了缓存,但读请求随后把旧数据写入了缓存)。
- 第一步: 更新数据库前,先删除缓存(或更新后立即删除)。
- 第二步: 休眠一小段时间(如 200ms)。
- 第三步: 再次删除缓存(防止第一步删除后,第二步之前有其他请求把旧数据写回缓存)。
在 Laravel 中实现双删的优缺点
优点:
- 理论上的最终一致性: 在高并发、跨进程或跨机器的异步更新中,双删能有效降低“缓存穿透”或“脏数据”的窗口期。
缺点:
- 实现复杂: 需要引入延迟任务(如 Laravel 的
dispatch()->delay(now()->addSeconds(0.2)))、队列(Queue)或sleep()函数。sleep()会阻塞进程,不推荐。 - 性能损失: 第二次删除是异步的,增加了系统复杂度。
- 非必须: 对于大多数 CRUD 应用,单次删除配合数据库事务(Transaction)和模型事件已经足够,双删在并发量极大(如秒杀、库存系统)或读多写少但写后读频繁的场景下才更有价值。
何时建议手动实现双删?
如果你遇到了以下情况,可以考虑在 Laravel 项目中实现类似双删的逻辑:
- 极高并发: 在秒杀、库存扣减、实时排行榜等场景,数据库和缓存之间存在严重的读写竞争。
- 缓存与数据库强一致性要求: 业务要求缓存与新写入的数据库数据在极短时间内(毫秒级)完全一致。
- 使用了多级缓存: 如本地缓存(Laravel 自带的
file或array驱动)配合 Redis,双删可以更彻底地清理各级缓存。
Laravel 中的双删示例(使用队列):
use App\Jobs\ClearUserCacheJob;
use Illuminate\Support\Facades\Cache;
public function updateUser($id, $data)
{
// 1. 更新数据库(事务内)
\DB::transaction(function () use ($id, $data) {
User::where('id', $id)->update($data);
// 2. 立即删除缓存(第一次删除)
Cache::forget('user:' . $id);
// 3. 延迟再删除缓存(双删)
ClearUserCacheJob::dispatch($id)->delay(now()->addSeconds(0.3));
});
}
// app/Jobs/ClearUserCacheJob.php
class ClearUserCacheJob implements ShouldQueue
{
public function __construct(public int $userId) {}
public function handle(): void
{
// 延迟后再次删除
Cache::forget('user:' . $this->userId);
}
}
总结建议
| 策略 | Laravel 内置支持 | 推荐场景 | 实现成本 |
|---|---|---|---|
| 单次删除(Cache::forget) | ✅ 原生支持,配合模型事件 | 大多数常规 Web 应用 | 低 |
| 双删策略 | ❌ 需要手动实现(队列 + 延迟) | 极高并发、强一致性要求 | 中-高 |
| 缓存标签(Tags) | ✅ 原生支持(需 Redis/Memcached) | 批量更新(如用户所有缓存) | 低-中 |
Laravel 不强制使用双删策略,对于绝大多数 Laravel 项目,使用模型事件 + Cache::forget() 已经足够应对缓存一致性问题,只有当你的项目确实面临极高的并发写入和读取竞争,并且对一致性有严格需求时,再考虑引入队列实现的“延时双删”。