Laravel创建可推送数组的缓存及缓存与数据库同步方案咨询
在Laravel中保持缓存与数据库同步的优化方案
你的代码已经用到缓存锁处理并发初始化问题,但仍存在缓存与数据库状态不一致(比如订单过期未同步)、异常处理缺失等潜在问题,以下是针对性的优化方案:
一、解决缓存与订单状态不同步问题
现有缓存仅在首次为空时拉取有效订单ID,但后续订单状态变更(如标记为expired)不会同步到缓存,导致缓存数据过时。可以从两方面处理:
- 保留缓存过期时间(你当前设置的1小时可根据业务调整),确保缓存定期自动刷新
- 在订单状态变更的逻辑中,主动更新缓存:当订单被标记为
expired时,从缓存数组中移除对应ID
示例代码(订单状态变更时):
// 标记订单为过期 $order->status = 'expired'; $order->save(); // 原子更新缓存,移除该订单ID $cachedIds = Cache::pull('cachedIds'); if ($cachedIds) { $cachedIds = array_filter($cachedIds, fn($id) => $id !== $order->id); Cache::put('cachedIds', $cachedIds, 60 * 60); }
二、完善缓存锁的异常处理
原代码未处理锁获取失败的情况,若锁超时或无法获取会直接抛出异常中断请求。需要增加异常捕获,保证请求友好降级:
三、确保订单创建与缓存更新的原子性
如果订单创建过程中出现错误(如验证失败、数据库异常),缓存不应被错误更新。可以将订单保存和缓存更新放在数据库事务中,保证要么都成功,要么都回滚。
四、优化缓存初始化逻辑
原代码用implode+explode转换数组的方式不够高效,直接用pluck获取数组更简洁。
优化后的完整代码
$maxLock = 60; try { $lock = Cache::lock("cacheLock", $maxLock); // 尝试获取锁,超时则返回繁忙提示 if (!$lock->get()) { abort(503, '系统繁忙,请稍后重试'); } // 初始化缓存:优先从缓存取,为空则从数据库拉取有效订单ID数组 $cachedIds = Cache::remember('cachedIds', 60 * 60, function () use ($product) { return $product->orders()->whereNot('status', 'expired')->pluck('id')->toArray(); }); // 订单创建与缓存更新的原子性处理 DB::beginTransaction(); try { $order = new ProductOrder(); $order->fill($request->all()); // 可在此添加订单验证逻辑 $order->save(); // 将新订单ID推送到缓存 $cachedIds[] = $order->id; Cache::put('cachedIds', $cachedIds, 60 * 60); DB::commit(); } catch (\Exception $e) { DB::rollBack(); throw $e; } // 及时释放锁 $lock->release(); } catch (\Illuminate\Contracts\Cache\LockTimeoutException $e) { abort(503, '系统繁忙,请稍后重试'); } return to_route('checkout', $product->url);
额外同步建议
- 使用缓存标签(若驱动支持):如果用Redis等支持标签的缓存驱动,可给
cachedIds打上product-orders-{$product->id}标签,批量变更订单时直接清空标签下的缓存,避免逐个更新 - 缩短锁持有时间:锁内只做必要的缓存初始化和更新操作,不要把耗时逻辑(如复杂查询)放在锁内
- 定期刷新缓存:通过Laravel任务调度定期执行缓存刷新,避免高峰时大量请求直接查询数据库
内容的提问来源于stack exchange,提问作者Misael
相关产品推荐
相关产品推荐

