我在MYSQL数据库中有一个流表。它具有以下列 taskId、wName、key、status、envNo、updateTime 等。没有与其他表连接。它具有以下索引:index1 (taskId)、index2 (wName、key、name、evnNo)、index3 (name)、index4 (key)、index5 (updateTime)、index6 (envNo)
查询1:
Below query giving me 11 records
select * from Flow where status = 0 and wName = 'abc' order by updateTime LIMIT 50
Running Explain on above query giving below response:
type: index, possible_key: index2, actual_key: index5, rows : 3000, Filtered: 0.05, Extra : Using Where
查询2:
Below query giving me 50 records
select * from Flow where status = 0 and wName = 'xyz' order by updateTime LIMIT 50
Running Explain on above query giving below response:
type: index, possible_key: index2, actual_key: index5, rows : 108, Filtered: 1.84, Extra : Using Where
以下是 Flow 表中指定 wName 的总可用记录。
select count(*) from Flow where wName = 'abc' - it gives me 3000 records.
select count(*) from Flow where wName = 'xyz' - it gives me 120000 records.
这里的问题是查询 1 在 20 秒内响应,而查询 2 在不到 1 秒内响应。为什么会这样?与查询 2 相比,查询 1 的数据非常少。
当我删除时LIMIT 50
,两个查询都会在几毫秒内响应。
当我删除时order by updateTime
,两个查询都会在几毫秒内响应。
请注意,我无法更新索引,因为除了这种情况之外一切都运行正常。
到目前为止,我已经找到了以下根本原因。
- 行可能在存储中分布不均,从而导致碎片化。
- 由于选择性低,MySQL 可能无法有效使用索引。
如果以上是原因,我该如何证明这些原因?如果这不是原因,可能的原因是什么?
根据您的评论,您最感兴趣的是了解为什么 Query1 比 Query2 慢,即使它返回的行较少,所以我从那开始。
使用索引
updateTime
(即按该列排序),您的查询将遍历表并查找符合您条件的行。Query2 在找到 50 行后停止。
Query1 无法提前停止,因为您的表中只有 13 行可以容纳,因此它必须读取整个表直到最后。这显然比提前停止要慢。
为了测试这一点,您可以将限制增加到大于 Query2 可以获得的行数的值(因此它也不能提前停止),并且您应该获得两者的相似执行时间 - 除非 MySQL 决定采用不同的索引。
您的查询的一个最佳索引是
(wName, status, updateTime)
。但是由于您无法(或不想)添加它,因此从可用索引列表中进行选择(并对数据分布进行一些假设),我猜索引
wName, ...
最好。由于大多数行似乎都不同于status
0,因此按排序updateTime
可能并不像 MySQL 认为的那样相关。这可能也是您删除时 MySQL 选择的索引
LIMIT 50
(尽管您没有提到主键是什么,这也可能是一种选择)。您可以强制 MySQL 使用该索引,例如尝试
Query2 现在会比 Query1 慢,但可能不会比现在慢。
请注意,这取决于您选择的值和数据分布,例如,如果您使用相同的查询
where status = 1
(或最常见的查询),则当前索引可能会更快(尽管我猜不会更快)。如果您将此查询用于不同情况,请记住这一点(并进行测试),例如,如果用户可以为“状态”选择一个值,并且您根据用户的选择运行此查询。