MySQL“;在;子查询的查询速度非常慢,但显式值的查询速度非常快

MySQL“;在;子查询的查询速度非常慢,但显式值的查询速度非常快,mysql,query-optimization,Mysql,Query Optimization,我有一个MySQL查询(Ubu 10.04、Innodb、Core i7、16Gb RAM、SSD驱动器、MySQL参数优化): 表em_link_数据大约有700万行,em_link有几千行。 完成此查询大约需要18秒。但是,如果我替换结果 并执行以下操作: SELECT COUNT(DISTINCT subscriberid) FROM em_link_data WHERE linkid in (24899,24900,24901,24902); 然后查询将在不到1毫秒的时间内运行。仅子查

我有一个MySQL查询(Ubu 10.04、Innodb、Core i7、16Gb RAM、SSD驱动器、MySQL参数优化):

表em_link_数据大约有700万行,em_link有几千行。 完成此查询大约需要18秒。但是,如果我替换结果 并执行以下操作:

SELECT
COUNT(DISTINCT subscriberid)
FROM
em_link_data
WHERE
linkid in (24899,24900,24901,24902);
然后查询将在不到1毫秒的时间内运行。仅子查询在不到1ms的时间内运行,列linkid已编制索引


如果我将查询重写为联接,也不到1ms。为什么有子查询的“IN”查询速度如此之慢,而有值的查询速度如此之快?我不能重写查询(购买的软件),所以我希望有一些调整或提示来加速这个查询!非常感谢您的帮助。

是的,中带子查询的
速度较慢。改为使用联接

SELECT
COUNT(DISTINCT subscriberid)
FROM em_link_data JOIN em_link ON em_link_data.linkid=em_link.id
WHERE em_link.campaignid = '2900' AND em_link.link != 'open'

并确保您已经在
em\u link\u data.linkid
em\u link.id
子查询上定义了索引,每次评估它们时都会执行这些子查询(无论如何,在MySQL中,不是所有的RDBMSE),也就是说,您基本上运行了700万个查询!如果可能的话,使用联接会将其减少到1。即使添加索引提高了这些查询的性能,您仍然在运行它们。

问题是MySQL从外部到内部执行查询,而您可能认为您的子查询只执行一次,然后将其结果传递给外部查询的WHERE表达式(请参阅)

如果无法重写查询,则应执行以下优化:

  • 正如FormsDesigner所说,在
    活动ID
    链接
    上添加一个索引
  • 通过执行
    EXPLAIN SELECT…
  • 启用并调整查询缓存,因为这样会加快子查询被多次调用的速度

另一个想法是安装并编写一个小脚本,截取您的查询并重写它以使用联接。

如果您的子查询速度很快,那么活动ID和链接绝对会被索引。 l、 id为PK并聚集,因此速度很快。 但据我记忆所及(上次我检查这个主题时),mysql描述了它对“in”子查询的内部优化,以使用子查询结果的索引排序来提高性能,并对“in”的左侧使用缓存为了更快地将其拖动到子查询中,并且如果将索引设置为true,则使用内部联接或“IN”而不是缓存,这可能是由于缓存问题和大量数据造成的。

我不知道软件的情况,但如果您可以使用内部联接,并且(可能)在外部查询的WHERE子句中的IN子句之前有一些额外的定义,请确保通过临时内部联接将该子句移动到主内部联接的before,其行为类似于中介“WHERE”子句,并减少联接中的交叉比较次数,如下所示:

SELECT ... FROM t
INNER JOIN (SELECT 1) AS tmp ON t.asd=23
INNER JOIN t2 ON ...
SELECT ... FROM t
INNER JOIN (SELECT ... FROM t2 WHERE constant clauses) AS tbl2 ON ...
正常和临时联接查找的示例比较:1000*1000>1000+(100*1000)

此外,子查询似乎是通过常量VAL过滤的,因此,如果是我,我会将子句放在生成结果集的子查询中,并减少联接中的比较次数,如下所示:

SELECT ... FROM t
INNER JOIN (SELECT 1) AS tmp ON t.asd=23
INNER JOIN t2 ON ...
SELECT ... FROM t
INNER JOIN (SELECT ... FROM t2 WHERE constant clauses) AS tbl2 ON ...
无论如何,在“in”查询中,将子查询中的表的任何列与外部查询中的表的任何列进行比较需要对两侧的列进行精确索引(关于复合索引),但这仍然可能是一个缓存问题

编辑:
我还好奇地问:在l.campaignid、l.link和l.id上建立综合指数有什么意义吗?

你的解释计划怎么说?您配置了哪些索引?我猜
em\u link
需要一个包含
activity id
link
的索引。这是优化器结果:选择count(distinct
ackci
em\u link\u data
subscriberid
)作为
count(distinct subscriberid)
来自
ackci
em\u link\u data
其中(
ackci
em\u link\u data
linkid
,((
ackci
em\u link\u data
linkid
)在em\u链接的主位置(
ackci
l
活动ID
<2900”)和(
ackci
link
'open')和(
ackci
em\u link\u data
linkid
)=
ackci
l
id
>))我相信MySQL 5.6.7通过
物化
选项解决了这个问题。如果子查询独立于外部查询,那么它将执行一次,在内部转换为临时表,然后连接到外部查询。MySQL一直是一个非常令人沮丧的问题,Oracle成功地解决了这个问题几十年前。对不起,从来没有做过任何基准测试…只是从经验中了解到这一点。第二件事是,如果我将子查询更改为它应该返回的4个值,我的速度会非常快。优化器是否弄乱了执行顺序?@Scott-看到了,但是为什么第二个示例在我提供子查询结果的地方会非常快?mySQL不会先执行子查询,然后执行主查询?我想优化器是在做什么…如果它先执行子查询并得到一个包含4个值的列表,然后执行主查询,那么应该可以…我在所有建议的列中都有索引…@Franco-Yep优化器在这些方面的性能非常差。请参阅@Franco:在第二个示例中,您提供了四个标量值。无需执行子查询,比较四个整数的速度非常快。是的,4个标量的列表不是子查询,它只是看起来有点模糊,有点像oneMartin,谢谢您指点我,你真是棒极了!我搜索过,但没有合适的“语言”来搜索它。这解释了因为它是一个优化器错误,否则它就毫无意义了。@Brian:I p