【问题标题】:Please Help Me With Mysql Slow Query Analysis请帮帮我Mysql慢查询分析
【发布时间】:2010-01-13 01:18:45
【问题描述】:

我有这个我想要分析的 mysql 查询。它很慢,这里的访问者表大约有 50K 条目,这个查询永远不会返回。当我尝试解释语句时,我发现访问者表上没有使用索引,尽管索引可用。现在这是我需要帮助解决的大难题。任何提示表示赞赏。

查询:

select distinct
  visitor0_.ID as ID130_,      

  case when visitor0_1_.id is not null then 1 when
  visitor0_.ID is not null then 0
  end as clazz_

from Visitor visitor0_ 
left outer join Operator visitor0_1_ on visitor0_.ID=visitor0_1_.id
where (visitor0_.ID not in
    (select operator1_.id 
     from Operator operator1_ 
     inner join Visitor operator1_1_ on operator1_.id=operator1_1_.ID))
  and (exists 
    (select visitorpro2_.ID 
     from VisitorProfileField visitorpro2_, ProfileField profilefie3_ 
     where visitorpro2_.profileFieldID=profilefie3_.ID 
       and visitorpro2_.visitorID=visitor0_.ID 
       and profilefie3_.name='subscription86' 
       and visitorpro2_.numberVal=1 
       and visitorpro2_.stringVal='Manual'))

解释输出屏幕截图: http://grab.by/grabs/9c3a629a25fc4e9ec0fa54355d4a092c.png

【问题讨论】:

  • 我重新格式化了 SQL,这样就不必水平滚动来阅读它了。
  • 酷,感谢比尔重新格式化。

标签: mysql performance entity-attribute-value sql-execution-plan


【解决方案1】:

根据我对您的查询的推断,以下应该产生相同的结果,没有子查询并且性能更快。

select v.ID as ID130_, 0 as clazz_
from Visitor v
left outer join (VisitorProfileField vpf join ProfileField pf 
                   on vpf.profileFieldID = pf.ID)
  on v.ID = vpf.visitorID and pf.name='subscription86' 
    and vpf.numberVal=1 and vpf.stringVal='Manual'
left outer join Operator o on v.ID = o.ID
where o.ID IS NULL;

如果我弄错了,请解释一下。您的 NOT IN 谓词似乎排除了与 Operator 中的任何 id 匹配的任何 Visitor id。也就是说,子查询会生成两个表中的所有 id 的列表,因此NOT IN 条件相当于到Operator 的外连接和o.ID IS NULL 的简单测试。

这意味着您的选择列表中的CASE 表达式毫无意义,因为如果您的条件仅匹配与Operator 中的任何行不匹配的Visitor 行,它肯定为0。

我认为您的查询中存在严重混淆。

此外,您似乎在 VisitorProfileFieldProfileField 表中使用了 EAV 反模式。这会给你带来很多麻烦。

【讨论】:

  • 这些查询不是我手动生成的。我从使用休眠编写的java应用程序中获取这些。我确实意识到它们与您提到的外部连接是冗余的。但我只是喜欢保留它,因为我无法更改原始应用程序源代码。但最终,如果一切都失败了,我将不得不走这条路。我试图简化这个查询并将其简化为最简单的类似查询,但它也不起作用。这是在下一条评论中。
  • 从访问者 primary_v 中选择 id,其中 id 不在(从 Operator 中选择 Operator.id)和(存在(从 VisitorProfileField a 中选择 a.ID,ProfileField b 其中 a.profileFieldID=b.ID 和 a .visitorID=primary_v.ID and b.name='subscription86' and b.numberVal=1 and a.stringVal='Manual')))
  • 请注意,当在第一个'and'之前单独尝试第一个选择时,它确实使用索引。只有当我添加存在语句时,整个事情才会崩溃。
  • 您应该能够将存在替换为连接和不同。尝试:从 Visitor primary_v、VisitorProfileField a、ProfileField b 中选择不同的 id,其中 a.profileFieldID=b.ID and a.visitorID=primary_v.ID and b.name='subscription86' and b.numberVal=1 and a.stringVal=' Manual' 和 primary_v.id 不在(从 Operator 中选择 Operator.id)
【解决方案2】:

您的查询...很大。你能解释一下它为你完成了什么吗?看起来它会提取每个访问者 ID,以及他们是否是操作员,而不是操作员,并且他们有特定的配置文件设置。这没有多大意义,所以我一定是错过了什么。

这是我的尝试,基于我对您正在尝试做的事情的理解:

select distinct visitor.ID, IF(operator.id IS NOT NULL, 1, 0) AS clazz
from Visitor left outer join Operator on visitor.ID = operator.id
where not exists 
    (select 'x' from Operator OperatorTwo where OperatorTwo.id = visitor.ID)
and exists
    (select 'x' from VisitorProfileField, ProfileField
        where VisitorProfileField.profileFieldID = ProfileField.ID
        and VisitorProfileField.profileFieldID.visitorID = visitor.ID
        and VisitorProfileField.profileFieldID.numberVal = 1
        and VisitorProfileField.profileFieldID.stringVal = 'Manual'
        and ProfileField .name = 'subscription86')

似乎没有使用名为“operator1_1_”的连接表,您应该可以将其删除。如果您使用它只是为了确保该表中有访问者的记录,我会使用存在而不是连接。我放弃了。

我已将您的 not 切换为 not exists,我认为这可能更容易让 MySQL 优化。我使用了 IF 而不是 case,因为你只有两个,而且打字更短。我不知道任何一个在 MySQL 上是否更快/更容易。

我可以告诉你,根据我的经验,MySQL 性能会随着 suqueries 中的子查询而下降。它似乎放弃了优化它们并开始逐行运行它们。我敢打赌,如果您使用临时结果表(仅用于测试目的),您会发现查询运行得更快。

编辑:

比尔比我走得更远,我走得还不够远。我喜欢 Bill 的提问,并且同意他关于 CASE 陈述的结论,这让我有点吃惊。

【讨论】:

  • MBCook,感谢您的回答。请参阅我对 Bill 的回复,我在其中进一步简化了查询。它只是试图挑选非运营商且订阅了邮件列表的访问者。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-07
  • 2011-07-19
  • 2011-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多