这里也存在此问题:Poor whereHas performance in Laravel ...但是没有答案。
我遇到的情况与此问题的作者类似:
replays
表具有4M行players
表有4000万行此查询使用where exists
,并且需要大量时间(70s)来完成:
select * from `replays`
where exists (
select * from `players`
where `replays`.`id` = `players`.`replay_id`
and `battletag_name` = 'test')
order by `id` asc
limit 100;
但是当更改为使用where id in
而不是where exists
时-更快(0.4s):
select * from `replays`
where id in (
select replay_id from `players`
where `battletag_name` = 'test')
order by `id` asc
limit 100;
正在使用MySQL(InnoDB)。
我想了解为什么where exists
与where id in
之间在性能上有如此大的差异-是因为MySQL的工作方式吗?我希望“存在”变体会更快,因为MySQL只会检查相关行是否存在...但是我错了(在这种情况下,我可能不理解“存在”是如何工作的。)
答案 0 :(得分:2)
您应该显示执行计划。
要优化exists
,您需要在players(replay_id, battletag_name)
上建立索引。 replays(id)
上的索引也应该有帮助-但如果id
是主键,则已经有一个索引。
答案 1 :(得分:1)
戈登有一个很好的答案。事实是,性能取决于许多不同的因素,包括数据库设计/架构和数据量。
作为粗略的指导,exists
子查询将对replays
中的每一行执行一次,而in
子查询将执行一次以获得结果子查询,然后将在replays
中的每一行中搜索这些结果。
因此,对于exists
,索引/访问路径越好,它将运行得越快。如果没有相关的索引,它将只读取所有行,直到找到匹配项。对于replays
中的每一行。对于没有匹配项的行,它将最终每次都读取整个players
表。在找到匹配项之前,即使具有匹配项的行也可以通读大量players
。
使用in
时,子查询的结果集越小,它将更快地运行。对于不匹配的用户,只需快速检查小的子查询行即可找到该答案。那就是说,您没有从索引中受益(如果它能以这种方式工作),因此对于子查询中的大型结果集,它必须先读取子选择中的每一行,然后再确定何时不匹配。
也就是说,数据库优化器非常聪明,并且并不总是按照您要求的方式评估查询,因此,为什么要检查执行计划并进行自我测试对于找出最佳方法很重要。期望某条执行路径只是为了发现优化器根据期望数据的外观选择了另一种执行方法,这并不稀奇。