巨大的性能差异:“WHERE x IN(... values ...)”与“WHERE x IN(<subquery>)”

时间:2016-12-06 12:13:44

标签: mysql sql query-optimization

我想查询表T以查找由某个列x标识的行。应该返回行的x的值由表U上的某个子查询确定,该查询带有列a上的实际过滤条件(短字符串)和b(时间戳值,寻找范围)。

SELECT * FROM T
    WHERE x IN (
        SELECT x FROM U WHERE a = ? AND b BETWEEN ? AND ?
    )
    ORDER BY x, y

TU都非常大(在1M..100M行的范围内)。他们的定义在问题的最后。在典型的用例中,x的约100个不同值的结果集中约有500到1000行。 x的这些值连续,实际上非常“随机”。

此查询需要相当长的时间(在0.5到2秒的范围内),尽管使用了表TU的正确索引,实际上只有少量行正在使用根据慢查询日志“检查”(大约1000)。

指数定义为:

    表格idx_u上的
  • 索引U涵盖了WHERE子句(ab)中的列。
  • idx_t上的索引T涵盖x列(用于查找行)和y(用于二级标准)。

上述查询的EXPLAIN为:

id  select_type  table  type   possible_keys  key    key_len  ref   rows  Extra     
1   SIMPLE       U      range  idx_u          idx_u  24       NULL  107   Using where; Using index; Using temporary; Using filesort
1   SIMPLE       T      ref    idx_t          idx_t  8        U.x   4     NULL

我还尝试了JOIN而不是子查询:

SELECT * FROM T
    JOIN U ON T.x = U.x
    WHERE a = ? AND b BETWEEN ? AND ?
    ORDER BY T.x, T.y

EXPLAIN与上面的内容完全相同。

但是,当单独执行子查询并按字面列出x的值时,两个查询一起花费的时间要少得多(在10到20毫秒的范围内)。此外,他们的EXPLAIN看起来不同。

第一个查询(最初是子查询):

SELECT x FROM U WHERE a = ? AND b BETWEEN ? AND ?

EXPLAIN与上面的第一个条目匹配:

id  select_type  table  type   possible_keys  key    key_len  ref   rows  Extra     
1   SIMPLE       U      range  idx_u          idx_u  24       NULL  107   Using where; Using index; Using temporary; Using filesort

现在是第二个查询:

SELECT * FROM T
    WHERE x IN (
        3917,8525,13149,17729,22355,26908,31457,36053,40663,45250 -- ... (64 values here)
    )
    ORDER BY x, y

解释为

id  select_type  table  type   possible_keys  key    key_len  ref   rows  Extra     
1   SIMPLE       T      range  idx_t          idx_t  8        U.x   436   Using index condition

现在我注意到在原始(撰写)查询中,对于表T,行数的猜测只有4,这太低了。此外,T的类型为ref,而在单独的查询中(x的显式值),则为range

T中查找错误的行号是否会导致查询以次优方式执行? ref类型是否会产生巨大的性能差异?

最后,为了解决这个问题,我可以强制range吗?请注意,这不是强制使用索引,因为它似乎已经被使用了。

编辑:表格是这样创建的:

CREATE TABLE `U` (
  `x` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `a` varchar(20) CHARACTER SET ascii NOT NULL,
  `b` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  -- (more columns irrelevant here)
) ENGINE=InnoDB;

ALTER TABLE `U`
  ADD PRIMARY KEY (`x`),
  ADD KEY `idx_u` (`a`,`b`) USING BTREE;
CREATE TABLE `T` (
  `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `x` bigint(20) UNSIGNED NOT NULL,
  `y` int(11) UNSIGNED NOT NULL,
  -- (more columns irrelevant here)
) ENGINE=InnoDB;

ALTER TABLE `T`
  ADD PRIMARY KEY (`id`),
  ADD KEY `idx_t` (`x`,`y`) USING BTREE;

2 个答案:

答案 0 :(得分:1)

如果您将查询更改为使用WHERE EXISTS而不是IN运算符,并且已尝试使用JOIN转换

,该怎么办?
SELECT * FROM T
    WHERE EXISTS (SELECT 1 FROM U WHERE T.x = U.x
                  AND a = ? 
                  AND b BETWEEN ? AND ?
    )
    ORDER BY x, y;

奇怪的事实是,我发现您在x,yADD KEY idx_t (x,y) USING BTREE)上有一个索引,但您的解释显示它正在执行文件分析Using where; Using index; Using filesort

答案 1 :(得分:0)

您可以尝试加入

{{1}}