我有一个问题:
SELECT abt.duration, a.id, a.a_id, a.a_tag
FROM active_begin_times AS abt INNER JOIN sources AS a ON a.id = abt.a_source
AND a.u IN (29, 28, 27, 26, 25, 24)
WHERE (abt.duration > 86400000)
and (abt.begin_timestamp<=1465185600000 and 1465617600000<=abt.end_timestamp
OR 1465185600000<=abt.begin_timestamp and abt.begin_timestamp<=1465617600000
OR 1465185600000<=abt.end_timestamp and abt.end_timestamp<=1465617600000)
order by abt.begin_timestamp asc LIMIT 0, 10
该数据库有大约500万条目。此查询大约需要30秒才能运行。但如果我按照我的订单更改为持续时间,则会在几秒内计算,如下面的查询:
SELECT abt.duration, a.id, a.a_id, a.a_tag
FROM active_begin_times AS abt INNER JOIN sources AS a ON a.id = abt.a_source
AND a.u IN (29, 28, 27, 26, 25, 24)
WHERE (abt.duration > 86400000)
and (abt.begin_timestamp<=1465185600000 and 1465617600000<=abt.end_timestamp
OR 1465185600000<=abt.begin_timestamp and abt.begin_timestamp<=1465617600000
OR 1465185600000<=abt.end_timestamp and abt.end_timestamp<=1465617600000)
order by abt.duration asc LIMIT 0, 10
我在持续时间和 begin_timestamp 上都有索引。当我将订单改为abt.id时,它又花了很多时间来计算,也有索引。
另外,我注意到的是这个特定的条件集,查询返回2行。但是如果我改变变量并且我得到这个查询返回20到30个奇数行,那么计算是瞬时的。
有人可以解释一下,以上2个案例?我试着查看,但无法理解行为或对解释不满意。第一个查询的EXPLAIN
返回:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|----|-------------|-------|--------|----------------------------------------------------------|-----------------|---------|-----------------------|------|-------------|
| 1 | SIMPLE | abet | index | FK2681F9347A6A34B,begin_timestamp,end_timestamp,duration | begin_timestamp | 9 | \N | 6094 | Using where |
| 1 | SIMPLE | a | eq_ref | PRIMARY,FK722DBCA9F603AF | PRIMARY | 4 | db.abt.alarm_source | 1 | Using where |
从我得到的第二个查询:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
|----|-------------|-------|--------|----------------------------------------------------------|----------|---------|-----------------------|------|------------------------------------|
| 1 | SIMPLE | abet | range | FK2681F9347A6A34B,begin_timestamp,end_timestamp,duration | duration | 9 | \N | 8597 | Using index condition; Using where |
| 1 | SIMPLE | a | eq_ref | PRIMARY,FK722DBCA9F603AF | PRIMARY | 4 | db.abt.alarm_source | 1 | Using where |
我可以看到的明显差异在Extras
列中,在2个查询中Using index condition;
,我不知道该怎么做。
SHOW CREATE TABLE的输出active_begin_time:
CREATE TABLE active_begin_times (
id int(11) NOT NULL AUTO_INCREMENT,
begin_timezone_offset int(11) DEFAULT NULL,
begin_timezone_suffix varchar(100) DEFAULT NULL,
begin_timestamp bigint(20) DEFAULT NULL,
begin_timestamp_date varchar(100) DEFAULT NULL,
duration bigint(20) DEFAULT NULL,
end_timezone_offset int(11) DEFAULT NULL,
end_timezone_suffix varchar(100) DEFAULT NULL,
end_timestamp bigint(20) DEFAULT NULL,
end_timestamp_date varchar(100) DEFAULT NULL,
incomplete int(11) DEFAULT NULL,
a_source int(11) DEFAULT NULL,
PRIMARY KEY (id),
KEY FK2681F9347A6A34B (alarm_source),
KEY begin_timestamp (begin_timestamp),
KEY end_timestamp (end_timestamp),
KEY begin_timezone_offset (begin_timezone_offset),
KEY end_timezone_offset (end_timezone_offset),
KEY duration (duration),
KEY begin_timestamp_date (begin_timestamp_date),
KEY end_timestamp_date (end_timestamp_date)
) ENGINE=MyISAM AUTO_INCREMENT=6164640 DEFAULT CHARSET=latin1
答案 0 :(得分:1)
对于第一个查询,索引用于按索引指定的顺序查找和读取整个表行,然后针对每一行测试整个where子句。所以它就像索引指定的顺序的全表扫描一样。
第二个更快,因为它使用索引来限制第一个地方(type: range)
的行,并且它在存储引擎级别(extras: using index condition)
执行此操作而不读取整个表要测试(abt.duration > 86400000)
的行。对于那些与第一个条件匹配的行,将读取完整的表行以测试其余的where子句。
如果不比较解释结果,如果比较where子句的两个部分,很容易就可以使用第一部分(abt.duration > 86400000)
的索引,但不能使用第二部分abt.begin_timestamp<=1465185600000
,而不是第二部分1465185600000<=abt.begin_timestamp
1}}和end_timestamp
甚至是begin_timestamp
上的第三个条件而不是Average Duration (days) :=
CALCULATE (
AVERAGEX ( Cases, MAX ( DateTable[Month Ending] ) - Cases[dtopened] ),
FILTER ( Cases, Cases[OpenClosedFlag] = "Open" ),
FILTER ( Cases, Cases[dtopened] <= MAX ( DateTable[Month Ending] ) )
)
。
答案 1 :(得分:1)
WHERE duration > 1234 AND ...
ORDER BY duration LIMIT 10
可以非常有效地使用INDEX(duration)
:
WHERE
其余部分的下10行。然后停止。注意:如果还有其他过滤(AND ...
),和,那么即将推出10行,这很快。另一方面,如果10行排在表格的后面,那么这很慢。您的其他情况更复杂,因为begin_timestamp
不在简单的where子句中。相反,它必须:
WHERE
匹配的所有行(EXPLAIN
估算数千)。begin_timestamp
(没有索引有用)事实上,这个可能比上面的“慢”情况更快。它取决于数据值的分布以及优化器不够了解的其他内容。所以......有时候优化器会选择“错误”的方式来评估这种查询,并且最终会比应该的速度慢。
关于“覆盖”索引的评论不正确,因为它不包含a_source
。
答案 2 :(得分:0)
因为你的表在duration
上有一个索引(而不是另一个),所以它获得了前10行,并且完成了。事实上,它有一个covering
索引,非常快。它是composite
密钥的一部分,满足查询中的所有内容,没有跳转到数据页面。当然,有一个加入。涉及两张表。