我们使用的是MySQL 5.5.42.
我们有一个publications包含大约1.5亿行的表(SSD上大约140 GB).
该表有许多列,其中两列特别有用:
id 是表的主键,是类型 bigintcluster_id 是一个可以为空的列 bigint两列都有自己的(单独的)索引.
我们对表单进行查询
SELECT * FROM publications
WHERE id >= 14032924480302800156 AND cluster_id IS NULL
ORDER BY id
LIMIT 0, 200;
Run Code Online (Sandbox Code Playgroud)
这是问题:
id值越大(上例中的14032924480302800156),请求越慢.
换句话说,低值请求id是快速的(<0.1 s),但id值越高,请求越慢(最多几分钟).
如果我们在WHERE子句中使用另一个(索引的)列,那么一切都很好.例如
SELECT * FROM publications
WHERE inserted_at >= '2014-06-20 19:30:25' AND cluster_id IS NULL
ORDER BY inserted_at
LIMIT 0, 200;
Run Code Online (Sandbox Code Playgroud)
哪里inserted_at是类型timestamp.
编辑:
EXPLAIN使用时输出id >= 14032924480302800156:
id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
---+-------------+--------------+------+--------------------+------------+---------+-------+----------+------------
1 | SIMPLE | publications | ref | PRIMARY,cluster_id | cluster_id | 9 | const | 71647796 | Using where
Run Code Online (Sandbox Code Playgroud)
EXPLAIN使用时输出inserted_at >= '2014-06-20 19:30:25':
id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
---+-------------+--------------+------+------------------------+------------+---------+-------+----------+------------
1 | SIMPLE | publications | ref | inserted_at,cluster_id | cluster_id | 9 | const | 71647796 | Using where
Run Code Online (Sandbox Code Playgroud)
对于 MySQL 以错误的顺序使用索引,存在一些猜测。PRIMARY索引的处理方式似乎与其他索引完全不同。
在带有主键条件的查询中可以使用索引PRIMARY和on 。cluster_id由于某种原因,MySQL 忽略PRIMARY索引并首先查看索引cluster_id,其中有一个条件:它应该是NULL。这给我们留下了一个巨大的潜在无序(NULL到处都是!)的行集来过滤id。
然而,对于下一个查询,情况有所不同:PRIMARY根本无法使用索引,因此 MySQL 以更好的方式计算出该使用什么,显然首先使用索引而inserted_at没有任何提示。
它在第一个查询中实际应该做的是PRIMARY首先获取索引(告诉它这样做)。我不是 MySQL 用户,我所有的猜测仅基于我自己对内部数据结构的理解。我不知道它是否可以在cluster_id结果之上应用索引,但是创建复合索引并比较使用和不使用它的性能可能会提供有关是否使用它的线索。
| 归档时间: |
|
| 查看次数: |
1307 次 |
| 最近记录: |