【发布时间】:2017-02-18 15:12:11
【问题描述】:
我有以下 MySQL 查询,在 linux VM 上大约需要 40 秒:
SELECT
* FROM `clients_event_log`
WHERE
`ex_long` = 1475461 AND
`type` in (2, 1) AND NOT
(
(category=1 AND error=-2147212542) OR
(category=7 AND error=67)
)
ORDER BY `ev_time` DESC LIMIT 100
该表大约有 700 万行。大小为 800 MB,它对 WHERE 和 ORDER BY 子句中使用的所有字段都有索引。
现在,如果我更改查询以在外部 SELECT 中完成排序,一切都会更快(大约 100 毫秒):
SELECT res.* FROM
(
SELECT * FROM `clients_event_log`
WHERE
`ex_long` = 1475461 AND
`type` in (2, 1) AND NOT
(
(category=1 AND error=-2147212542) OR
(category=7 AND error=67)
)
) AS res
ORDER BY res.ev_time DESC LIMIT 0, 100
您知道为什么第一个查询需要这么长时间吗?谢谢。
稍后更新:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE clients_event_log index category,ex_long,type,error,categ_error ev_time 4 NULL 5636 Using where
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> system NULL NULL NULL NULL 1
2 DERIVED clients_event_log ref category,ex_long,type,error,categ_error ex_long 5 131264 Using where
表定义:
CREATE TABLE `clients_event_log` (
`ev_id` int(11) NOT NULL,
`type` int(6) NOT NULL,
`ev_time` int(11) NOT NULL,
`category` smallint(6) NOT NULL,
`error` int(11) NOT NULL,
`ev_text` varchar(1024) DEFAULT NULL,
`userid` varchar(20) DEFAULT NULL,
`ex_long` int(11) DEFAULT NULL,
`client_ex_long` int(11) DEFAULT NULL,
`ex_text` varchar(1024) DEFAULT NULL,
PRIMARY KEY (`ev_id`),
KEY `category` (`category`),
KEY `ex_long` (`ex_long`),
KEY `type` (`type`),
KEY `ev_time` (`ev_time`),
KEY `error` (`error`),
KEY `categ_error` (`category`,`error`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
【问题讨论】:
-
当然不应该这样。查询在语义上是相等的,并且应该导致相同的执行计划。由于显然不是这样,我认为这是 DBMS 中的一个缺陷。
-
您可能在所有字段上都有单个索引,例如
ex_long上的一个索引,type上的一个索引,ev_time上的一个索引。 MySQL 每个查询只能使用一个索引,并且可能在您的第一个查询中使用了错误的索引(例如ev_time上的索引)。查看explain输出以检查(并将其添加到您的问题中)。因此,要解决您的问题,您需要一个复合索引,例如ex_long, ev_time(取决于您的数据,也可能是其他列)或强制使用正确的单列索引(可能在您的第二个查询中使用的索引)。 -
第一个执行计划很愚蠢(通过索引访问 每个 记录只是为了按最终排序的顺序读取它们。)正如我所说: MySQL 优化器中的缺陷。
-
@ThorstenKettner 你是对的,它不应该有所作为,但在 MySQL 中,它确实如此。这是旧 mysql 版本中创建派生表的方式,因此 MyQSL 不会在内部查询中使用现在的外部
order by-index。 5.7会正确执行。 -
@Julian 您的
explain显示,正如预期的那样,您在查询中使用了不同的索引。您可以添加复合索引(根据需要添加任意数量的列,但我猜 2 应该没问题),或者(或者如果由于某种原因它仍然无法工作)您可以使用from `clients_event_log` force index (ex_long)强制正确的索引您的第一个查询。
标签: mysql sql select sql-order-by