在Laravel项目中,当我们需要获取模型关联数据时,经常会遇到N+1查询问题,或者频繁访问数据库导致性能瓶颈。如果关联数据本身更新频率很低,把关联查询的结果存入缓存就能有效减少数据库压力,提升接口响应速度。

直接缓存关联查询结果
最简单的方式是先执行关联查询,再把整个结果集存入缓存。这种方式适合关联数据整体变动少的场景,比如文章对应的分类、作者信息很少修改的情况。
我们可以通过Cache门面来实现,示例代码如下:
<?php
namespace AppHttpControllers;
use IlluminateSupportFacadesCache;
use AppModelsPost;
class PostController extends Controller
{
public function index()
{
// 缓存键可以自定义,这里用posts_with_relations作为标识
$cacheKey = 'posts_with_relations';
// 缓存时间设置为1小时,单位秒
$cacheTtl = 3600;
$posts = Cache::remember($cacheKey, $cacheTtl, function () {
// 先预加载关联,避免N+1查询
return Post::with(['category', 'author'])->get();
});
return response()->json($posts);
}
}
上面的代码中,Cache::remember方法会先检查缓存中是否存在对应的键,如果存在就直接返回缓存内容,不存在则执行闭包里的查询逻辑,然后把结果存入缓存再返回。
按单个模型缓存关联数据
如果关联数据需要单独更新,整体缓存的方式会导致缓存命中率下降,这时候可以按单个模型来缓存对应的关联数据。比如每个文章的分类信息单独缓存,当某个分类更新时,只需要清除对应文章的关联缓存即可。
示例代码如下:
<?php
namespace AppModels;
use IlluminateDatabaseEloquentModel;
use IlluminateSupportFacadesCache;
class Post extends Model
{
// 定义文章和分类的关联
public function category()
{
return $this->belongsTo(Category::class);
}
// 获取文章的缓存分类信息
public function getCachedCategory()
{
$cacheKey = "post_{$this->id}_category";
return Cache::remember($cacheKey, 3600, function () {
return $this->category;
});
}
// 清除当前文章的关联缓存
public function clearRelationCache()
{
Cache::forget("post_{$this->id}_category");
}
}
当分类模型更新时,我们可以遍历关联的所有文章,调用clearRelationCache方法清除对应的缓存:
<?php
namespace AppObservers;
use AppModelsCategory;
use AppModelsPost;
class CategoryObserver
{
public function updated(Category $category)
{
// 获取所有关联该分类的文章
$posts = Post::where('category_id', $category->id)->get();
foreach ($posts as $post) {
$post->clearRelationCache();
}
}
}
缓存关联预加载的查询构造器结果
有时候我们不需要缓存整个模型集合,只需要缓存关联预加载时执行的某一条查询的结果,这时候可以自定义关联的定义,在关联方法里加入缓存逻辑。
示例代码如下:
<?php
namespace AppModels;
use IlluminateDatabaseEloquentModel;
use IlluminateSupportFacadesCache;
class User extends Model
{
// 定义用户的文章关联,加入缓存逻辑
public function posts()
{
return $this->hasMany(Post::class)->where(function ($query) {
$userId = $this->id;
$cacheKey = "user_{$userId}_posts";
// 这里用remember缓存查询结果
$query->whereIn('id', Cache::remember($cacheKey, 3600, function () use ($userId) {
return Post::where('user_id', $userId)->pluck('id')->toArray();
}));
});
}
}
缓存失效的注意事项
使用缓存关联查询时,一定要做好缓存失效的处理,否则会出现数据不一致的问题。常见的失效场景包括:
- 主模型或关联模型新增、修改、删除时,需要清除对应的关联缓存
- 如果关联数据有批量更新的操作,要批量清除相关的缓存键
- 可以设置合理的缓存过期时间,作为兜底的数据一致性保障
我们可以在模型的观察者或者仓库层的更新方法里统一处理缓存清除逻辑,避免遗漏。
性能对比参考
下面是一组简单的性能测试数据,对比不使用缓存和使用缓存的关联查询耗时:
| 场景 | 查询次数 | 平均耗时(毫秒) |
|---|---|---|
| 不使用缓存,无预加载 | 101次(1次查主表,100次查关联) | 320 |
| 不使用缓存,有预加载 | 2次(1次查主表,1次查关联) | 45 |
| 使用缓存关联查询 | 0次(缓存命中时) | 8 |
从数据可以看出,合理使用缓存关联查询,能把接口耗时降低到原来的几十分之一,效果非常明显。
Laravel缓存关联查询Eloquent关联预加载query_cache修改时间:2026-07-21 09:51:34