MySQL并不总是使用索引

非常简单的问题,但很难找到解决方案.
具有2,498,739行的地址表具有min_ip和max_ip字段.这些是表的核心锚点,用于过滤.

查询非常简单.

SELECT * 
FROM address a 
WHERE min_ip < value
  AND max_ip > value;

因此,为min_ip和max_ip创建索引以使查询更快是合乎逻辑的.

为以下内容创建的索引.

CREATE INDEX ip_range ON address (min_ip, max_ip) USING BTREE;
CREATE INDEX min_ip ON address (min_ip ASC) USING BTREE;
CREATE INDEX max_ip ON address (max_ip DESC) USING BTREE;

我确实尝试仅创建第一个选项(min_ip和max_ip的组合),但是它没有用,所以我准备了至少3个索引,以便为MySQL提供更多选择索引的选项. (请注意,此表几乎是静态的,更多的是查找表)

+------------------------+---------------------+------+-----+---------------------+-----------------------------+
| Field                  | Type                | Null | Key | Default             | Extra                       |
+------------------------+---------------------+------+-----+---------------------+-----------------------------+
| id                     | bigint(20) unsigned | NO   | PRI | NULL                | auto_increment              |
| network                | varchar(20)         | YES  |     | NULL                |                             |
| min_ip                 | int(11) unsigned    | NO   | MUL | NULL                |                             |
| max_ip                 | int(11) unsigned    | NO   | MUL | NULL                |                             |
+------------------------+---------------------+------+-----+---------------------+-----------------------------+

现在,应该直接使用min_ip和max_ip作为过滤条件查询表.

EXPLAIN
SELECT * 
FROM address a 
WHERE min_ip < 2410508496
  AND max_ip > 2410508496;

该查询执行了大约0.120到0.200秒的时间.但是,在负载测试中,查询会迅速降低性能.
MySQL服务器的CPU使用率猛增到100%,同时执行少量查询会导致CPU使用率迅速下降,并且性能无法迅速提高.
mysql服务器上的慢速查询已打开10秒钟或更长时间,最终,负载测试几秒钟后,最终在日志中显示了选择查询.
所以我检查了带有解释的查询,发现它没有使用索引.

解释计划结果

    id  select_type  table   type    possible_keys           key     key_len  ref        rows  Extra        
------  -----------  ------  ------  ----------------------  ------  -------  ------  -------  -------------
     1  SIMPLE       a       ALL     ip_range,min_ip,max_ip  (NULL)  (NULL)   (NULL)  2417789  Using where  

有趣的是,它能够将ip_range,ip_min和ip_max确定为潜在索引,但从未使用过任何关键列中所示的索引.
我知道我可以使用FORCE INDEX并尝试在其上使用解释计划.

EXPLAIN
SELECT * 
FROM address a 
FORCE INDEX (ip_range)
WHERE min_ip < 2410508496
  AND max_ip > 2410508496;

用FORCE INDEX结果说明计划

    id  select_type  table   type    possible_keys  key       key_len  ref        rows  Extra                  
------  -----------  ------  ------  -------------  --------  -------  ------  -------  -----------------------
     1  SIMPLE       a       range   ip_range       ip_range  4        (NULL)  1208894  Using index condition  

使用FORCE INDEX,是的,它使用ip_range索引作为键,并且行显示查询中不使用FORCE INDEX的子集,该子集是2,417,789中的1,208,894.
因此可以肯定,使用索引应该具有更好的性能. (除非我误解了解释结果)

但是更有趣的是,经过几次测试,我发现在某些实例中,即使没有FORCE INDEX,MySQL也会使用索引.我的观察是,当值较小时,它将使用索引.

EXPLAIN
SELECT * 
FROM address a 
WHERE min_ip < 508496
  AND max_ip > 508496;

说明结果

    id  select_type  table   type    possible_keys           key       key_len  ref       rows  Extra                  
------  -----------  ------  ------  ----------------------  --------  -------  ------  ------  -----------------------
     1  SIMPLE       a       range   ip_range,min_ip,max_ip  ip_range  4        (NULL)       1  Using index condition  

因此,让我感到困惑的是,基于传递给选择查询的值,MySQL决定何时使用索引以及何时不使用索引.
我无法想象确定何时对传递给查询的某个值使用索引的基础是什么.我明白
如果在WHERE条件下没有合适的匹配索引,则可能不使用索引,但是在这种情况下,很明显ip_range索引
是基于min_ip的索引,在这种情况下max_ip列适用于WHERE条件.

但是我有更大的问题是,其他查询呢.我是否必须去大规模测试那些查询.
但是即使那样,随着数据的增长,我是否可以依靠并期望MySQL使用该索引?
是的,我总是可以使用FORCE INDEX来确保它使用索引.但这不是适用于所有数据库的标准SQL.
ORM框架在生成SQL时可能无法支持FORCE INDEX语法,它将查询与索引名称紧密结合在一起.

不知道是否有人遇到过这个问题,但这对我来说似乎是一个很大的问题.

解决方法:

完全同意Vatev和其他人的观点.不仅MySQL做到了.扫描表有时比先查看索引然后查找磁盘上的相应条目便宜.

它唯一确定使用索引的时间是当它是覆盖索引时,这意味着查询中的每一列(对于此特定的表)都存在于索引中.意思是,如果仅需要例如“网络”列

SELECT network
FROM address a 
WHERE min_ip < 2410508496
  AND max_ip > 2410508496;

然后是覆盖指数

CREATE INDEX ip_range ON address (min_ip, max_ip, network) USING BTREE;

只会查看索引,因为根本不需要在磁盘上查找其他数据.整个索引可以保存在内存中.

上一篇:MySQL,SELECT * FROM与索引列


下一篇:mysql-在一个表上添加多个索引是否对插入有害?