N+1查询必然发生于未预加载关联时,必须在get()前用with()、select需包含外键、事务中禁用with()、withCount()用于计数、load()用于补救加载。

并发场景下直接用 Eloquent 默认写法,大概率会触发死锁、重复处理、缓存击穿或事务内预加载失效——这不是配置问题,是设计模式错位。
事务里别调 with(),否则查不到数据还卡住
在 DB::transaction() 里写 User::with('profile')->get(),表面没报错,实际可能:查询被隔离级别阻塞、关联数据查不到、甚至整个事务超时回滚。Eloquent 的懒加载在事务中静默触发,Debugbar 都不显示子查询。
- 必须提前把关联数据拿全:
$users = User::with('profile')->get();,再进事务块 - 真要在事务里关联查询,改用
DB::table('users')->join('profiles', ...),自己控制 SQL - 如果业务强依赖顺序(比如按创建时间处理),
chunkById()不适用,得手写WHERE created_at > ? AND id > ?分页逻辑
select() 漏掉外键字段,关联就断了
你以为只选几个字段省资源,结果 User::select('name')->with('posts')->get() 返回的每个 $user->posts 都是空集合——因为主模型没带 id,Eloquent 根本没法匹配外键。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 主模型
select()必须包含主键(通常是id)和所有外键字段(如user_id) - 关联模型也得限制字段,且闭包里必须含其外键:
with(['posts' => fn ($q) => $q->select('id', 'title', 'user_id')]) - 多对多关系(如
roles)要显式选pivot字段:select('id', 'name', 'role_user.level')
高并发下 chunk() 会跳行或死锁
chunk(500, ...) 基于 LIMIT OFFSET,在并发写入/删除频繁的表上,分页偏移会漂移,一批数据可能被漏掉或重复处理;更糟的是,MySQL 可能因间隙锁冲突直接卡住。
- 一律换
chunkById(500, ...),它按主键范围推进,稳定 - 但前提是主键必须是带索引的整型自增 ID;UUID 或字符串主键不支持
-
chunkById()固定按id ASC推进,不能配合orderBy('updated_at')——想按时间处理,得建联合索引(updated_at, id),再手写条件分页
缓存键太宽泛,一更新全失效
写 User::remember(3600)->get() 看似省事,结果用户 A 改了邮箱,整个缓存要等 1 小时才刷新,期间所有用户看到的都是旧数据。
- 缓存键必须带业务语义,比如
Cache::remember('active_users_202605', 3600, fn() => User::where('active', 1)->get()) - 涉及关联的数据,缓存键要覆盖所有影响源:
posts_with_authors_202605,而不是笼统叫posts_all - 更新操作后,主动
Cache::forget(...)对应键,别等过期
并发不是加个锁就能解决的事;Eloquent 的抽象层在高压力下会暴露很多隐性耦合。最危险的坑,往往发生在你没意识到“这里其实有数据库交互”的地方——比如模型的访问器、全局作用域、甚至 toArray() 调用时触发的懒加载。

















