Mysql索引被忽略了

时间:2009-06-03 17:11:55

标签: sql mysql indexing primary-key key

EXPLAIN SELECT
*
FROM
content_link link
STRAIGHT_JOIN
content
ON
link.content_id = content.id
WHERE
link.content_id = 1
LIMIT 10;

+----+-------------+---------+-------+---------------+------------+---------+-------+------+-------+
| id | select_type | table   | type  | possible_keys | key        | key_len | ref   | rows | Extra |
+----+-------------+---------+-------+---------------+------------+---------+-------+------+-------+
|  1 | SIMPLE      | link    | ref   | content_id    | content_id | 4       | const |    1 |       |
|  1 | SIMPLE      | content | const | PRIMARY       | PRIMARY    | 4       | const |    1 |       |
+----+-------------+---------+-------+---------------+------------+---------+-------+------+-------+

但是,当我删除WHERE时,查询将停止使用该键(即使我明确强制它)

EXPLAIN SELECT
*
FROM
content_link link FORCE KEY (content_id)
STRAIGHT_JOIN
content
ON
link.content_id = content.id
LIMIT 10;

+----+-------------+---------+--------+---------------+---------+---------+------------------------+---------+-------------+
| id | select_type | table   | type   | possible_keys | key     | key_len | ref                    | rows    | Extra       |
+----+-------------+---------+--------+---------------+---------+---------+------------------------+---------+-------------+
|  1 | SIMPLE      | link    | index  | content_id    | PRIMARY | 7       | NULL                   | 4555299 | Using index |
|  1 | SIMPLE      | content | eq_ref | PRIMARY       | PRIMARY | 4       | ft_dir.link.content_id |       1 |             |
+----+-------------+---------+--------+---------------+---------+---------+------------------------+---------+-------------+

对此有什么解决方法吗?

我意识到我在第二个例子中选择了整个表,但是为什么mysql突然决定它会忽略我的FORCE并且不使用密钥?没有密钥,查询需要10分钟......呃。

3 个答案:

答案 0 :(得分:4)

索引有助于在表格内快速搜索,但如果选择整个表格,它只会减慢速度。所以MySQL忽略索引是正确的。

在您的情况下,索引可能具有MySQL不知道的隐藏副作用。例如,如果内部联接仅保留几行,则索引会加快速度。但是,如果没有明确的提示,MySQL就无法知道。

有一个例外:当您选择的每个列都在索引中时,如果您选择每一行,索引仍然有用。例如,如果您在LastName上有索引,则以下查询仍然可以从索引中受益:

select LastName from orders

但是这个不会:

select * from Orders

答案 1 :(得分:4)

FORCE有点用词不当。以下是MySQL文档所说的内容(强调我的):

您还可以使用FORCE INDEX,其作用类似于USE INDEX(index_list),但另外还假设表扫描非常昂贵。换句话说,只有在无法使用某个给定索引在表中查找行时才使用表扫描。

由于您实际上并没有“找到”任何行(您正在全部选择它们),因此表格扫描始终将是最快的,并且优化程序足够智能以便尽快知道你告诉他们的是什么。

ETA:

尝试在主键上添加一次ORDER BY,我敢打赌它会使用索引。

答案 2 :(得分:0)

您的content_id似乎接受NULL值。

MySQL优化器认为不能保证您的查询仅使用索引返回所有值(尽管实际上有保证,因为您使用了JOIN中的列)

这就是它恢复全表扫描的原因。

添加NOT NULL条件:

SELECT  *
FROM    content_link link FORCE KEY (content_id)
STRAIGHT_JOIN
        content
ON      content.id = link.content_id
WHERE   link.content_id IS NOT NULL
LIMIT 10;

或将您的专栏标记为NOT NULL

ALTER TABLE content_link MODIFY content_id NOT NULL

<强>更新

MySQL已对此进行了验证bug 45314