我有一个名为devicelog的MySQL表,其id
上有PK,但device_id
(INT),field_id
(INT)和unixtime
上有多个索引(BIGINT) )。它们只是默认的InnoDB索引。
我试图在一定时间内获取ID,我得到不同的性能与不同的值和不同的ORDER BY。 ID和unixtimes都有正关联,因为随着更多数据的插入,它们都在增加,所以似乎可以安全地省略unixtime上的排序。我的表有大约2500万条记录,性能非常重要。
此查询相当慢(~0.5秒):编辑:使用USE INDEX(unixtime)
后,我能够提高性能(<0.01秒!)。
SELECT
id
FROM
devicelog
USE INDEX(unixtime) /* edit: looking at the EXPLAIN, I can use this index and it sped things up a bit */
WHERE
device_id = 26
AND field_id = 64
AND unixtime >= 1397166634707 /* a fairly recent time */
/* with no ORDER BY clause, this query is surprisingly slow */
LIMIT 1
说明:
1, SIMPLE, devicelog, index_merge, device_id,field_id,field_id_2,unixtime, field_id,device_id, 8,8, , 6667, Using intersect(field_id,device_id); Using where
此查询非常快(<0.01秒):
SELECT
id
FROM
devicelog
WHERE
device_id = 26
AND field_id = 64
AND unixtime >= 1397166634707 /* a fairly recent time */
ORDER BY unixtime ASC /* <- using unixtime to order */
LIMIT 1
说明:
1, SIMPLE, devicelog, range, device_id,field_id,field_id_2,unixtime, unixtime, 9, , 897776, Using index condition; Using where
如何省略ORDER BY会降低性能?认为它会提高速度似乎是合乎逻辑的。
然而,如果我将unixtime更改为远程返回到“1”,当我使用ORDER BY unixtime时它会完全变慢。我相信unixtime索引是按升序排序的,所以这也没有多大意义。
此查询的执行方式与上述查询相反。
非常快(<0.01秒):
SELECT
id
FROM
devicelog
WHERE
device_id = 26
AND field_id = 64
AND unixtime >= 1 /* a long time ago */
LIMIT 1
说明:
1, SIMPLE, devicelog, index_merge, device_id,field_id,field_id_2,unixtime, field_id,device_id, 8,8, , 6742, Using intersect(field_id,device_id); Using where
此查询与快速查询完全相同,只是它使用较旧的时间:
非常缓慢(约7秒):
SELECT
id
FROM
devicelog
WHERE
device_id = 26
AND field_id = 64
AND unixtime >= 1 /* a long time ago */
ORDER BY unixtime ASC /* <- using unixtime to order */
LIMIT 1
说明:
1, SIMPLE, devicelog, index, device_id,field_id,field_id_2,unixtime, unixtime, 9, , 3504, Using where
有没有人对广泛的性能差异有任何见解?
答案 0 :(得分:1)
我认为这是LIMIT优化的记录行为,请参阅http://dev.mysql.com/doc/refman/5.5/en/limit-optimization.html
优化LIMIT查询
MySQL有时会优化具有LIMIT row_count子句且没有HAVING子句的查询:
[...] 如果将LIMIT row_count与ORDER BY一起使用,MySQL会在找到排序结果的第一行row_count行后立即结束排序,而不是对整个结果进行排序。如果使用索引完成排序,则速度非常快。如果必须完成文件排序,则在找到第一个row_count之前,将选择与没有LIMIT子句的查询匹配的所有行,并对其中的大部分或全部进行排序。在找到初始行之后,MySQL不会对结果集的任何剩余部分进行排序。
[...]
只要MySQL向客户端发送了所需的行数,它就会中止查询,除非您使用的是SQL_CALC_FOUND_ROWS。
因为您试图在特定日期旁边获取ID,我认为排序结果非常重要,因为否则您可以获得任意值。否则,您必须根据条件使用MIN(id)来获得所需的id值。
答案 1 :(得分:1)
如果不知道表格中的行数以及表格的确切结构,很难就性能提出明确的建议。
您可以在(unixtime, device_id, file_id, id)
上尝试覆盖索引的复合词。 (如果你不知道那个词,请查看索引索引。)
这将允许您的查询的unixtime
部分满足BTREE查找,然后通过索引扫描可以满足您的查询的其余部分。
如果您指定ORDER BY unixtime ASC LIMIT 1
,则告诉查询引擎停止扫描该索引(一旦获得一次点击,就会unixtime
排序。
我不知道为什么当你省略ORDER BY时它有时会继续扫描七秒钟。它可能需要寻找匹配的device_id
和file_id
值。