mysql最常见的几种扫描方式
- system: 系统表,少量数据,往往不需要进行磁盘IO
- const:常量连接
- eq_ref:主键索引(primary key)或者非空唯一索引(unique not null)等值扫描
- ref:非主键非唯一索引等值扫描
- range:范围扫描
- index:索引树扫描
- ALL:全表扫描(full table scan)
上述各种扫描方式由快到慢依次为:
system > const > eq_ref > ref > range > index > ALL
上述个扫描方式的要点:
- system最快:不进行磁盘IO
- const:PK或者unique上的等值查询
- eq_ref:PK或者unique上的join查询,等值匹配,对于前表的每一行(row),后表只有一行命中
- ref:非唯一索引,等值匹配,可能有多行命中
- range:索引上的范围扫描,例如:between/in/>
- index:索引上的全集扫描,例如:InnoDB的count
- ALL最慢:全表扫描(full table scan)
可以通过explain查看type字段来判断使用的哪种扫描方式,从而决定是否进行优化
一、system
explain select * from mysql.time_zone;
从系统库mysql的系统表time_zone里查询数据,扫码类型为system,这些数据已经加载到内存里,不需要进行磁盘IO,所以速度是最快的。
二、const
const扫描的条件为:
(1)命中主键(primary key)或者唯一(unique)索引;
(2)被连接的部分是一个常量(const)值;
explain select * from user where id=1;
id是主键,1是常量,则扫描方式为const
这类扫描效率极高,返回数据量少,速度非常快。
三、eq_ref
eq_ref扫描的条件为,对于前表的每一行(row),后表只有一行被扫描。
再细化一点:
(1)join查询;
(2)命中主键(primary key)或者非空唯一(unique not null)索引;
(3)等值连接;
explain select * from user,user_ex where user.id=user_ex.id;(两边的id都为主键)
这种精确对应后表一行的查询效率也非常快。
四、ref
如果把上例中的主键都改为索引,那返回的结果就变成了ref,表示对弈前表的每一行,后表不一定是只有一行被扫描。
另外,当id改为普通非唯一索引后,常量的连接查询,也由const降级为了ref,因为也可能有多于一行的数据被扫描。
例:explain select * from user where id=1;
这里id不是主键,而是索引,所以扫描方式就变为了ref。
ref扫描,可能出现在join里,也可能出现在单表普通索引里,每一次匹配可能有多行数据返回,虽然它比eq_ref要慢,但它仍然是一个很快的join类型。
可见合理设置主键和索引的重要性。
五、range
range扫描就比较好理解了,它是索引上的范围查询,它会在索引上扫码特定范围内的值。
例如between,in,>都是典型的范围(range)查询。(这里用于查询的条件列要为索引,否则不能批量“跳过”)
六、index
index类型,需要扫描索引上的全部数据。
例如:explain count (*) from user;
id为主键,count查询需要扫描索引上的全部数据才可以。
效率比较慢,只是比全表扫描要好一点。
七、ALL
explain select * from user,user_ex where user.id=user_ex.id;
如果id上不建索引,对于前表的每一行(row),后表都要被全表扫描。
综合上面的七个例子可以发现:
join语句出现了三次:
- 如果id为主键,则扫描类型为eq_ref
- 如果id为非唯一普通索引,扫描类型为ref
- 如果非主键非索引,则扫描类型为ALL
查询效率相差很大,因此,可以注意到正确的建立索引,设置主键,对查询性能提升有很大的影响。