为什么MySQL在执行此查询时不使用索引?

为什么MySQL在执行此查询时不使用索引?,mysql,select,indexing,Mysql,Select,Indexing,但这样的话,它的用途是: mysql> desc users; +-------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+------------------+------+-----+---------+--------

但这样的话,它的用途是:

mysql> desc users;
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| id          | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| email       | varchar(128)     | NO   | UNI |         |                |
| password    | varchar(32)      | NO   |     |         |                |
| screen_name | varchar(64)      | YES  | UNI | NULL    |                |
| reputation  | int(10) unsigned | NO   |     | 0       |                |
| imtype      | varchar(1)       | YES  | MUL | 0       |                |
| last_check  | datetime         | YES  | MUL | NULL    |                |
| robotno     | int(10) unsigned | YES  |     | NULL    |                |
+-------------+------------------+------+-----+---------+----------------+
8 rows in set (0.00 sec)


mysql> create index i_users_imtype_robotno on users(imtype,robotno);
Query OK, 24 rows affected (0.25 sec)
Records: 24  Duplicates: 0  Warnings: 0
mysql> explain select * from users where imtype!='0' and robotno is null;
+----+-------------+-------+------+------------------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys          | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+------------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | users | ALL  | i_users_imtype_robotno | NULL | NULL    | NULL |   24 | Using where |
+----+-------------+-------+------+------------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
除此之外,这一个也没有使用索引:

mysql> explain select * from users where imtype in ('1','2') and robotno is null;
+----+-------------+-------+-------+------------------------+------------------------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys          | key                    | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+------------------------+------------------------+---------+------+------+-------------+
|  1 | SIMPLE      | users | range | i_users_imtype_robotno | i_users_imtype_robotno | 11      | NULL |    3 | Using where |
+----+-------------+-------+-------+------------------------+------------------------+---------+------+------+-------------+
1 row in set (0.01 sec)

这是因为“robotno”可能是主键,它使用主键而不是索引

mysql> explain select id,email,imtype from users where robotno=1;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | users | ALL  | NULL          | NULL | NULL    | NULL |   24 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
只有
(imtype,robotno)
的单个连续范围不能满足此条件

如果您有这样的记录:

SELECT  *
FROM   users
WHERE  imtype != '0' and robotno is null
,由
(imtype,robotno)
排序,则会返回记录
1
5
7
,而其他记录不会返回

您需要创建此索引以满足以下条件:

imtype  robotno

$       NULL
$       1    
0       NULL
0       1
1       NULL
1       1
2       NULL
2       1
并稍微重写您的查询:

CREATE INDEX ix_users_ri ON users (robotno, imptype)
此索引也将用于此查询:

robotno imtype  

--- first block start
NULL    $
--- first block end
NULL    0
--- second block start
NULL    1
NULL    2
--- second block end
1       $
1       0
1       1
1       2
SELECT id, email, imtype
FROM   users
WHERE  robotno = 1
,由于同样的原因,现在没有任何索引提供

实际上,此查询的索引是:

robotno imtype  

--- first block start
NULL    $
--- first block end
NULL    0
--- second block start
NULL    1
NULL    2
--- second block end
1       $
1       0
1       1
1       2
SELECT id, email, imtype
FROM   users
WHERE  robotno = 1

仅用于
imtype
上的粗略过滤(注意
extra
字段中使用where
),它不包括
robotno

顺便说一句,如果您认为您比优化器更了解,通常情况下,您可以通过添加


FORCE INDEX(INDEX\u name)
FROM users

数据库系统查询计划器通过分析查询where子句相对于索引的选择性来确定是否进行索引扫描。(索引也用于将表连接在一起,但这里只有
用户
。)

第一个查询具有
其中imtype!='0'
。这将选择
users
中几乎所有的行,假设您有大量不同的
imtype
值。不等式算子本质上是非选择性的。因此,MySQL查询规划器在这里打赌,读取索引不会有帮助,它还不如对整个表进行顺序扫描,因为它可能无论如何都必须这样做

另一方面,如果您说
其中imtype='0'
,equality是一个高度选择性的运算符,MySQL会打赌,只读取几个索引块,就可以避免读取
用户
表本身的几乎所有块。所以它会选择索引

mysql> explain select id,email,imtype from users where robotno=1;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | users | ALL  | NULL          | NULL | NULL    | NULL |   24 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
在您的第二个示例中,
其中imtype In('1','2')
,MySQL知道索引的选择性很高(虽然只有
中imtype='0'
的一半),并且它会再次打赌使用索引将带来巨大的回报,正如您所发现的

在第三个示例中,
其中robotno=1
,MySQL可能无法有效地使用
用户(imtype,robotno)
上的索引,因为它需要读取所有索引块才能找到
robotno=1
记录编号:索引首先按
imtype
排序,然后按
robotno
排序。如果您在
用户(robotno)
上有另一个索引,MySQL会急切地使用它

作为脚注,如果您有两个索引,一个在
用户(imtype)
上,另一个在
用户(imtype,robotno)
上,并且您的查询在
上,其中imtype='0'
,任何一个索引都会使您的查询速度加快,但MySQL可能会选择
用户(imtype)
因为它更紧凑,需要从中读取的块更少

我在这里太简单了。早期的数据库系统只会查看imtype的数据类型,并对查询的选择性做出非常粗略的猜测,但人们很快意识到,向查询计划器提供有趣的事实,如表的总大小、每列中ditinct值的数量等,将使其能够做出更明智的决策。例如,如果有一个
users
表,其中
imtype
仅为每个“0”或“1”,则查询计划器可能会选择索引,因为在这种情况下,
where imtype!='0'
更具选择性


看看MySQL UPDATE STATISTICS语句,您会发现它的查询计划器一定很复杂。出于这个原因,在使用FORCE语句向它口述查询计划之前,我会犹豫很多。相反,使用UPDATE STATISTICS为query planner提供改进的信息,以作为其决策的基础。

您需要一个以robotno作为第一列的索引。您现有的索引是(imtype,robotno)。因为imtype不在where子句中,所以它不能使用该索引

(robotno,imtype)上的索引可用于where子句中仅包含robotno的查询,也可用于where子句中包含imtype和robotno的查询(但imtype本身不包含)


查看上的文档,并查找有关多列索引和“最左侧前缀”的部分。

您的索引已超过
用户(imtype,robotno)
。要使用此索引,必须使用
imtype
imtype
robotno
来限定行。您只是在查询中使用
robotno
,因此它不能使用此索引。

在这种情况下,MySQL查询优化器确定使用int(10)比使用varchar(1)和int(10)的组合索引更快如果你想在头脑中这样做,你也会采用这种方法。不过,一定要使用全尺寸的生产数据集,测量手动优化前后的查询时间。你不想“悲观”你的SQL。除非您真的知道自己在做什么,否则不要在数据库系统上强制执行查询计划。