我们正在使用MySQL 5.5.42。
我们有一个表publications
,其中包含大约1.5亿行(SSD上大约140 GB)。
该表有许多列,其中两列特别有用:
id
是表格的主键,类型为bigint
cluster_id
是bigint
两列都有自己的(单独的)索引。
我们对表格进行查询
SELECT * FROM publications
WHERE id >= 14032924480302800156 AND cluster_id IS NULL
ORDER BY id
LIMIT 0, 200;
以下是问题:
id
值越大(上例中的14032924480302800156),请求就越慢。
换句话说,低id
值的请求很快(<0.1秒),但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;
其中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
使用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
答案 0 :(得分:3)
关于MySQL使用错误顺序的索引,有一些猜测。 PRIMARY
索引似乎与其他索引完全不同。
在具有主键条件的查询中,可以使用索引PRIMARY
和cluster_id
。出于某种原因,MySQL忽略PRIMARY
索引并首先查看cluster_id
上的索引,其中有条件:它应该是NULL
。这给我们留下了一个巨大的潜在无序(NULL
s无处不在!)行集,由id
过滤。
然而,对于下一个查询,它是不同的:PRIMARY
索引根本无法使用,因此MySQL以更好的方式计算使用的内容,显然首先使用inserted_at
上的索引而没有任何提示
在第一次查询中它应该实际执行的操作首先是PRIMARY
索引(tell it to do so)。我不是MySQL用户,我所有的猜测只能由我自己对内部数据结构的理解来支持。我不知道它是否可以在结果之上应用cluster_id
上的索引,但是创建一个复合索引并比较使用和不使用它的性能可能会提供有关它是否被使用的线索。
答案 1 :(得分:0)
性能问题很可能发生,因为您只有列id
和cluster_id
的单独索引,而不是这两列的索引同时存在。
对于更大的ID,cluster_id与请求的值(NULL)匹配的情况更少见?在这种情况下,对于较大的ID,数据库可能需要遍历更多记录,以便使用cluster_id IS NULL
选择所请求的200。
如果两列都有一个索引,则数据库不需要遍历那么多记录,因为它会知道哪些记录同时满足2个搜索条件。