繁体   English   中英

禁止MySQL对查询使用全表扫描

[英]Prohibit MySQL from using full table scan on a query

当使用索引找不到结果时,有什么方法可以禁止MySQL执行全表扫描?

例如这个查询:

SELECT *
FROM a
WHERE (X BETWEEN a.B AND a.C) 
ORDER BY a.B DESC 
LIMIT 1;

仅在X满足条件且返回至少1行时才有效,但如果表中的任何数据无法满足条件,则将执行完全扫描,这可能非常昂贵。

我不想优化这个特定的查询,它只是一个例子。

使用范围内或范围外的X来解析此查询:

id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE a range long_ip  long_ip 8 \N 116183 100.00 Using where

STATUS VARIABLE显示更好的信息。 对于超出范围的X:

Handler_read_prev 84181
Key_read_requests 11047

在范围内:

Handler_read_key 1
Key_read_requests 12

如果只有一种方法可以阻止Handler_read_prev过去1增长。

UPDATE。 我不能接受我自己的答案,因为它并没有真正回答这个问题(HANDLER是一个很棒的功能)。 在我看来,没有通用的方法来阻止MySQL进行全面扫描。 虽然像key ='X'这样的简单条件将被视为“不可能的地方”,但像BETWEEN这样的更复杂的事情则不会。

您可以编写一个“完全覆盖”的子查询,该子查询仅使用索引中可用的数据。 根据返回的主键,您可以在主表中查找行。

以下查询完全由(id),(B,id)和(C,id)上的索引覆盖:

select *
from a
where id in (
    select id
    from a 
    where x <= C
    and id in (
        select id
        from a
        where B <= X 
    )
)
limit 1

每个SELECT使用一个索引:最内层的索引(B,id); 中间的SELECT使用索引(C,id),外部SELECT使用主键。

以下是我最终提出的建议:

HANDLER a OPEN;
HANDLER a READ BC <= (X);
HANDLER a CLOSE;

BC是密钥(B,C)的名称。 如果我们通过B DESC对表进行排序,则保证结果等于

SELECT *
FROM a
WHERE (X BETWEEN a.B AND a.C) 
ORDER BY a.B DESC 
LIMIT 1;

现在,如果X不在表a的范围内,我们只需要检查aC是否大于X,如果不是,那么X绝对超出范围,我们不需要再看了。

虽然这不是很优雅,但您必须在每次插入或更新时使用该表。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM