NextJS+Prisma多对多查询速度过慢问题求助
问题:Prisma 查询包含特定配置的车辆时性能极差
我用NextJS开发项目,ORM采用Prisma,数据库为MariaDB。相关表结构如下:
model cars_archive { id Int @id @default(autoincrement()) ... inactivestamp DateTime? @db.DateTime(0) @@index([brand], map: "brand") @@index([model], map: "model") } model extras { id Int @id @default(autoincrement()) @db.MediumInt value String @unique(map: "value") @db.VarChar(255) extras_cars extras_cars[] } model extras_cars { car_id Int extra_id Int @db.MediumInt extras extras @relation(fields: [extra_id], references: [id], onUpdate: Restrict, map: "extras_cars_ibfk_1") cars cars @relation(fields: [car_id], references: [id], onUpdate: Restrict, map: "extras_cars_ibfk_2") @@id([car_id, extra_id]) @@index([car_id, extra_id], map: "car_id") @@index([extra_id], map: "extra_id") }
我需要查询包含['Cruise Control', 'ABS']两个配置的所有车辆,编写的Prisma查询如下:
const cars = await prisma.cars.findMany({ where: { extras_cars: { every: { extras: { value: { in: ['Cruise Control','ABS'] } } } } } })
该查询结果正确,但响应极慢(耗时15-20秒)。而我用原生SQL查询单配置仅需约1秒:
SELECT COUNT(*) FROM cars JOIN extras_cars ec on cars.id = ec.car_id JOIN extras e on e.id = ec.extra_id WHERE e.value='Cruise Control'
查看Prisma调试日志发现它生成了非常复杂的SQL,特此求助如何优化查询速度。
优化方案
1. 修正查询逻辑,改用聚合筛选(匹配多配置的正确姿势)
你当前使用every的逻辑是所有关联配置都必须在目标列表中,这和“车辆包含所有目标配置”的需求不符。正确逻辑应为:车辆关联的配置需覆盖所有目标项,可通过先筛选关联任意目标配置的车辆,再统计匹配数量等于目标数量的车辆来实现。
用Prisma聚合查询直接在数据库层完成筛选,避免内存处理:
const targetExtras = ['Cruise Control', 'ABS']; const targetCount = targetExtras.length; // 先获取符合条件的car_id列表 const matchingCarIds = await prisma.extras_cars.groupBy({ by: ['car_id'], where: { extras: { value: { in: targetExtras } }, }, having: { car_id: { _count: { equals: targetCount } } }, select: { car_id: true } }); // 再根据id查询车辆详情 const cars = await prisma.cars.findMany({ where: { id: { in: matchingCarIds.map(item => item.car_id) } } });
2. 直接执行原生SQL
既然原生SQL性能达标,Prisma支持直接执行原生查询,完全复用高效逻辑:
const targetExtras = ['Cruise Control', 'ABS']; const targetCount = targetExtras.length; const cars = await prisma.$queryRaw` SELECT c.* FROM cars c JOIN extras_cars ec ON c.id = ec.car_id JOIN extras e ON e.id = ec.extra_id WHERE e.value IN (${Prisma.join(targetExtras)}) GROUP BY c.id HAVING COUNT(DISTINCT e.value) = ${targetCount} `;
这种方式绕过Prisma的查询生成逻辑,性能和原生查询一致。
3. 优化索引配置
- 确认
extras.value的唯一索引已被正确利用:该字段的唯一索引可快速定位对应的extra_id - 给
extras_cars添加复合索引(extra_id, car_id),进一步优化关联查询速度 - 检查
cars.id的主键索引是否正常(默认主键自带索引,可通过数据库工具验证)
4. 减少不必要的数据传输
如果查询不需要返回车辆的所有关联配置,避免使用include,仅查询所需字段,降低数据处理和传输开销。
内容的提问来源于stack exchange,提问作者Thomas Lumesberger
相关产品推荐
相关产品推荐

