为什么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。除非您真的知道自己在做什么,否则不要在数据库系统上强制执行查询计划。