我想查询表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
表T
和U
都非常大(在1M..100M行的范围内)。他们的定义在问题的最后。在典型的用例中,x
的约100个不同值的结果集中约有500到1000行。 x
的这些值不连续,实际上非常“随机”。
此查询需要相当长的时间(在0.5到2秒的范围内),尽管使用了表T
和U
的正确索引,实际上只有少量行正在使用根据慢查询日志“检查”(大约1000)。
指数定义为:
idx_u
上的U
涵盖了WHERE
子句(a
,b
)中的列。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;
答案 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,y
(ADD KEY idx_t (x,y) USING BTREE
)上有一个索引,但您的解释显示它正在执行文件分析Using where; Using index; Using filesort
答案 1 :(得分:0)
您可以尝试加入
{{1}}