对于大N,MySQL(id> = N AND col2 IS NULL)查询意外地慢

时间:2015-07-16 09:33:40

标签: mysql performance

我们正在使用MySQL 5.5.42。

我们有一个表publications,其中包含大约1.5亿行(SSD上大约140 GB)。

该表有许多列,其中两列特别有用:

  • id是表格的主键,类型为bigint
  • cluster_idbigint
  • 类型的可空列

两列都有自己的(单独的)索引。

我们对表格进行查询

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

2 个答案:

答案 0 :(得分:3)

关于MySQL使用错误顺序的索引,有一些猜测。 PRIMARY索引似乎与其他索引完全不同。

在具有主键条件的查询中,可以使用索引PRIMARYcluster_id。出于某种原因,MySQL忽略PRIMARY索引并首先查看cluster_id上的索引,其中有条件:它应该是NULL。这给我们留下了一个巨大的潜在无序(NULL s无处不在!)行集,由id过滤。

然而,对于下一个查询,它是不同的:PRIMARY索引根本无法使用,因此MySQL以更好的方式计算使用的内容,显然首先使用inserted_at上的索引而没有任何提示

在第一次查询中它应该实际执行的操作首先是PRIMARY索引(tell it to do so)。我不是MySQL用户,我所有的猜测只能由我自己对内部数据结构的理解来支持。我不知道它是否可以在结果之上应用cluster_id上的索引,但是创建一个复合索引并比较使用和不使用它的性能可能会提供有关它是否被使用的线索。

答案 1 :(得分:0)

性能问题很可能发生,因为您只有列idcluster_id的单独索引,而不是这两列的索引同时存在。

对于更大的ID,cluster_id与请求的值(NULL)匹配的情况更少见?在这种情况下,对于较大的ID,数据库可能需要遍历更多记录,以便使用cluster_id IS NULL选择所请求的200。

如果两列都有一个索引,则数据库不需要遍历那么多记录,因为它会知道哪些记录同时满足2个搜索条件。