我有一个MySQL查询(Ubu 10.04,Innodb,Core i7,16Gb RAM,SSD驱动器,MySQL params优化):
SELECT
COUNT(DISTINCT subscriberid)
FROM
em_link_data
WHERE
linkid in (SELECT l.id FROM em_link l WHERE l.campaignid = '2900' AND l.link != 'open')
表em_link_data有大约7百万行,em_link有几千行。 此查询大约需要 18秒才能完成。但是,如果我替换结果 子查询并执行此操作:
SELECT
COUNT(DISTINCT subscriberid)
FROM
em_link_data
WHERE
linkid in (24899,24900,24901,24902);
然后查询将在不到1毫秒的时间内运行。子查询单独运行不到1毫秒,列linkid被编入索引。
如果我将查询重写为连接,也不到1毫秒。为什么“IN”查询的子查询速度如此之慢以及为什么这么快的值?我无法重写查询(购买软件)所以我希望有一些调整或提示来加速这个查询!任何帮助表示赞赏。
答案 0 :(得分:23)
每次评估它们时都会执行子查询(无论如何,在MySQL中,并非所有RDBMS),即你基本上运行了700万个查询!如果可能,使用JOIN会将此值减少为1.即使添加索引可以提高性能,也可以运行它们。
答案 1 :(得分:4)
是的,带有子查询的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_link_data.linkid
和em_link.id
上定义了索引。
答案 2 :(得分:4)
问题是MySQL从外到内执行查询,而您可能认为您的子查询已完成一次,然后其结果将传递给外部查询的WHERE表达式(请参阅MySQL documentation)。
如果无法重写查询,则应执行以下优化:
campaignid
和link
上添加索引,因为FrustratedWithFormsDesigner说EXPLAIN SELECT ...
还有一个想法是安装MySQL proxy并编写一个拦截查询的小脚本并重写它以使用连接。
答案 3 :(得分:0)
如果你的子查询很快,那么campaignid和链接绝对是索引的。 l.id是PK并且聚集因此很快。 但据我记得(从上次我检查过这个主题),mysql描述了它的内部优化" in"子查询使用子查询结果的索引排序来提高性能,并在" IN"的左侧使用缓存。将它拖到子查询中更快,如果索引设置为true,则它必须没有这种差异才能使用内连接或" IN"而不是缓存,这可能是由于缓存问题和海量数据。 http://dev.mysql.com/doc/internals/en/transformation-scalar-in.html
我不知道软件的情况,但如果您可以使用INNER JOIN并且在外部查询的WHERE子句中的IN子句之前(可能)有一些额外的定义,请确保将该子句移动到通过临时INNER JOIN进入主INNER JOIN之前的行为类似于干预"其中"子句顺序并减少JOIN中的交叉比较次数,如下所示:
SELECT ... FROM t
INNER JOIN (SELECT 1) AS tmp ON t.asd=23
INNER JOIN t2 ON ...
正常和临时联接查找的示例比较:1000 * 1000> 1000 +(100 * 1000)
此外,似乎子查询被常量val过滤,因此如果是我,我会将子句放在生成结果集的子查询中,并减少JOIN中的比较次数,如下所示:
SELECT ... FROM t
INNER JOIN (SELECT ... FROM t2 WHERE constant clauses) AS tbl2 ON ...
无论如何,在" IN"查询,将子查询中的表的任何列与外部查询中的表的任何列进行比较,需要精确索引双方的列(关于复合索引),但仍然可能是缓存问题。
已编辑: 我也很好奇地问:l.campaignid,l.link和l.id上的复合索引是否有意义?