Laravel数据库查询优化:按比例拉取直连及互关好友动态性能优化咨询
代码优化方案
原有代码存在3个核心问题:一是多次全量加载关联数据到内存做过滤,IO开销高;二是使用union查询会触发临时表排序,额外增加性能损耗;三是未实现业务要求的70%直连好友、10%互关好友的比例控制,结果占比不可控。可按以下方案优化:
- 第一步先补充基础索引,从底层降低查询开销:
following表加联合索引(follower_id, following_id),覆盖关注关系查询的过滤条件posts表加联合索引(profile_id, created_at),覆盖按发布者筛选、按时间排序的查询需求
- 第二步重构查询逻辑,去掉不必要的中间数据加载,拆分查询替代union:
function feedUpdate($perPage = 20) { $currentUserId = Auth::id(); // 子查询获取当前用户关注的id列表,不用全量加载到内存 $followingIdQuery = Following::where('follower_id', $currentUserId)->select('following_id'); // 按70%比例取直连好友动态,默认按发布时间倒序,可根据业务调整排序规则 $friendPostCount = ceil($perPage * 0.7); $friendPosts = Post::whereIn('profile_id', $followingIdQuery) ->orderByDesc('created_at') ->limit($friendPostCount) ->get(); // 按10%比例取互关/好友的好友动态 $mutualPostCount = ceil($perPage * 0.1); $mutualPosts = collect(); if ($mutualPostCount > 0) { // 先按共同关注数排序取top互关候选,避免全表随机性能差 $mutualIdList = Following::whereIn('follower_id', $followingIdQuery) ->whereNotIn('following_id', $followingIdQuery) // 排除已经关注的用户,避免内容重复 ->where('following_id', '!=', $currentUserId) // 排除自己 ->groupBy('following_id') ->orderByRaw('COUNT(*) DESC') ->limit($mutualPostCount * 5) // 多取部分候选再随机,保证内容多样性 ->pluck('following_id'); $mutualPosts = Post::whereIn('profile_id', $mutualIdList) ->inRandomOrder() ->limit($mutualPostCount) ->get(); } // 合并结果,不足perPage的部分可补充热门动态等内容凑数,可根据业务调整 $posts = $friendPosts->merge($mutualPosts); // 可选:合并后统一按发布时间排序,保证时间线合理性 return new FeedResourceCollection($posts); }
优化后查询次数从4次降到2-3次,且所有查询都走索引,不会加载全量好友数据到内存,性能提升非常明显,同时完全符合业务比例要求。
多表关联查询最佳实践
- 索引优先:所有where、join、group by、order by用到的字段都要配置索引,多条件过滤场景优先使用联合索引,遵循最左匹配原则,避免全表扫描
- 避免中间数据冗余:不要将全表/全量关联数据加载到应用内存再做过滤、聚合操作,尽量通过SQL子查询、聚合函数在数据库层面完成计算,减少数据IO开销
- 慎用高开销SQL操作:
union、inRandomOrder()、distinct、大表join这类操作开销很高,非必要不使用,必须使用的话要先把结果集过滤到最小范围再执行 - 避免N+1查询:Laravel场景下使用
with()、withCount()预加载关联数据,禁止在循环中执行关联查询 - 适当做字段冗余:Feed流这类读多写少的场景,可以把发布者昵称、头像等常用字段冗余到post表,不用每次查询都关联用户表,减少join操作
- 热点数据缓存:高频访问的feed列表、关注关系列表可以设置几分钟的缓存,不用每次请求都查库,大幅降低数据库压力
内容的提问来源于stack exchange,提问作者Yoseph
相关产品推荐
相关产品推荐

