Laravel使用HybridRelations对接MongoDB查询300万通知数据过慢如何优化
MongoDB查询优化方案
- 首先确认索引实际生效
先通过db.notifications.getIndexes()命令确认user_id索引已经创建完成,不存在未完成的异步索引构建任务。再给查询加上explain()执行计划分析,确认HybridRelations生成的查询语句确实命中了user_id索引,排除全表扫描的情况。如果你的Notification模型开启了软删除但没有将deleted_at加入索引,查询时会自动带上deleted_at is null条件导致单user_id索引失效。 - 构建匹配查询场景的复合索引
绝大多数通知查询都会按创建时间倒序拿最新数据,单独的user_id索引无法覆盖排序逻辑,MongoDB会在内存中对匹配到的所有用户通知做排序,数据量越大耗时越高。你可以根据常用查询场景创建复合索引:- 仅按用户查最新通知:创建
{user_id: 1, created_at: -1}复合索引,匹配user_id后可直接按索引顺序返回前N条数据,无需额外排序 - 常按已读/未读状态筛选:创建
{user_id: 1, read: 1, created_at: -1}复合索引,覆盖筛选+排序全链路
- 仅按用户查最新通知:创建
- 优化关联查询逻辑
检查HybridRelations的关联配置,明确指定关联的外键、本地键,避免扩展包自动猜测字段生成多余的查询条件。同时用Laravel调试工具确认实际执行的MongoDB查询仅为单条db.notifications.find({user_id: xxx}).limit(5),无多余的跨库关联查询。 - 用覆盖索引减少IO开销
查询时通过select()指定需要返回的字段,避免查询无用字段,示例代码:
如果查询字段都包含在复合索引中,MongoDB可以直接从索引返回数据,无需读取实际的文档数据,性能可提升1~2倍。$user->notifications()->select('description', 'priority', 'read', 'created_at')->take(5)->get(); - 优化MongoDB服务配置
确认MongoDB的WiredTiger缓存大小至少为索引总大小的2倍,确保热点索引和高频访问的数据可以全部加载到内存中,避免查询时频繁读取磁盘。 - 增加业务层缓存
用户通知的前几页访问频率极高,可将每个用户最新的20条通知存入Redis,设置1~5分钟的有效期,查询时优先读缓存,有新通知生成时主动更新缓存,绝大多数请求无需打到MongoDB即可拿到结果。
内容的提问来源于stack exchange,提问作者Behnam Aminazad
相关产品推荐
相关产品推荐

