【发布时间】:2016-12-06 12:13:43
【问题描述】:
我想在表 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) 根据慢查询日志。
索引定义为:
- 表
U上的索引idx_u涵盖WHERE子句(a、b)中的列。 - 表
T上的索引idx_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;
【问题讨论】:
-
该字段涉及什么数据类型,您能告诉我们创建索引吗?
-
@JuanCarlosOropeza 涉及的字段是整数类型(准确地说是
BIGINT)。我编辑了问题,希望您在底部找到相关信息。 -
尝试为
(a, b, x)创建索引,这样就不必查找x,并且您是否传递了正确的类型来与b进行比较? -
你说其他列无关紧要。好吧,他们不是——因为你这样做了
SELECT *。如果您选择TEXT或BLOB列,这很重要。因此,如果可以减少*以排除此类列,它可能运行得更快很多。请提供更多详细信息。
标签: mysql sql query-optimization