mysql 查询优化中,索引的使用原则
- 前提:sys_user 。数据340W
CREATE TABLE `sys_user` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名' ,
`user_password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户密码' ,
`phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '电话号码' ,
`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱' ,
`address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址' ,
PRIMARY KEY (`id`),
INDEX `idx_user_name` (`user_name`) USING BTREE
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
AUTO_INCREMENT=3409377
ROW_FORMAT=DYNAMIC
;
- 列的离散度
在分析索引的使用之前,先看一个必要的概念:列的离散度。
- 计算公式:user_name 的离散度
select (select count(distinct(user_name)) from sys_user )/(select count(1) from sys_user);
从上面公式可以看出:列重复的值越少,离散度越高,重复的值越多离散度越低。这个和索引有啥关系?结论是:离散度越高,在查询时,扫描的行数越少,效率越高。原理后面分析,先看现象。
- 离散度测试
数据量 340W。
如上图所示:email 的数据离散度非常低。也就是email 存中很多的重复的值。
- 在没有使用索引的前提下:查询
SELECT user_name,email from sys_user where email = '1234@163.com'
运行结果是,需要2.9s,我们可以使用 explain
分析一下执行情况。
EXPLAIN SELECT user_name,email from sys_user where email = '1234@163.com'
从图中我们可以看到,在没有使用索引的情况下,查询条件是email 时,要扫描 310W 行的数据,几乎是全表扫描了,所以很慢,用了接近3s 的时间。
- 给email 创建索引
ok, 从上面的查询结果看,查询很慢,我们第一反应是创建一个索引,ok,现在我们就来创建一个索引。
我们看到查询结果,结果明显只用于0.001s。那么是不是说明离散度的低的列建立索引也能对查询结果有明显的提高呢?先分析explain :
我们看到,这条语句执行只扫描了三行,所以查询很快。再看一条语句:
怎么回事?不是加了索引就会变快吗?怎么换个值查询,就又变慢了呢??原因就是扫描了150万行的数据。
- 多条件查询:
SELECT user_name,email, user_password from sys_user where email = '123@163.com' and user_password = '1234569'
查询结果:
简直就是灾难,明明只有两条数据符合结果,但是却用了接近10s 。
扫描数据库 157W行数据。
- 结论:从上面的运行结果看,表中的email 列数据离散度非常低,也就是说重复度非常高,那么我们就算为email 创建了索引,效果也没有多么明显,也就是说,如果想通过索引来优化查询性能的话,创建索引的列的值的重复度必须要小。