上一篇文章 简单的说了下数据库索引的结构,接下来当然要看一看索引到底是咋用的,要是建立了索引却没用,那不是太亏了不是?当然,慢一点不要就,就怕进行大批量的操作,数据量又不少,最后一直在加载表数据到缓存,直接把缓存挤爆了,那就惨了,哈哈~
那怎么判断一个查询语句有没有用索引呢?加了索引,难道就会用到?肯定不是啊,背面试题的时候,肯定要记,哦,索引加上左右‘%’会失效,使用聚合函数会失效等等等等。
那SQL到底用没用索引呢?这个要MySQL来告诉我们。每一个SQL执行前,MySQL会对这个SQL语句进行分析,然后确认查询方式,争取使用最快的速度查询目标数据!而在分析过程中之后,执行之前,自然有个东东,也就是咋执行,书面语就是 执行计划! 好吧,我们先来瞅瞅MySQL的执行计划到底是啥。
首先,先创建两个表。
- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`stu_name` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '学生姓名',
`stu_code` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '学生编号',
`school_code` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '学校编码',
`create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_code`(`stu_code`) USING BTREE COMMENT '学生编码索引'
) ENGINE = InnoDB AUTO_INCREMENT = 1446881 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci COMMENT = '学生表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`teacher_code` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '教师工号',
`teacher_name` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '教师名称',
`school_id` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '学校',
`create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `code`(`teacher_code`) USING BTREE COMMENT '教师索引',
UNIQUE INDEX `name`(`teacher_name`) USING BTREE COMMENT '名称索引'
) ENGINE = InnoDB AUTO_INCREMENT = 42100 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Dynamic;
-- 多对多,师生关系表
CREATE TABLE `relation` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`teacher_id` int(11) NULL DEFAULT NULL,
`student_id` int(11) NULL DEFAULT NULL,
`remark` varchar(40) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `key`(`teacher_id`, `student_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Dynamic;
创建两个存储过程,批量写数据,写的存储过程比较简单,可以尝试优化存储过程,使用批量插入,插入速度会快很多。我已经插入完成了,就不改了哈。
-- 存储过程插入学生表
CREATE PROCEDURE `batchInStu`( i INT )
BEGIN
DECLARE a int DEFAULT 1;
while a <= i DO
START TRANSACTION;
INSERT INTO student(stu_name, stu_code, school_code, create_time) VALUES (CONCAT('张三',a),REPLACE(UUID(),'-',''),20,CURRENT_TIMESTAMP);
COMMIT;
SET a = a+1;
end while;
end
搞定!学生表的话我大概插入了100多万条数据,教师表插入100条数据左右。基础设施建设完毕,正题开始!
如上图,查询某个SQL的查询效率,是否使用索引等信息,可以在整个查询语句之前,添加一个单词,explain!
查询的结果,一共有12个字段。
1.id列
这个其实就是查询的序列号,序号越大,执行的优先级越高,相同的话就从上往下执行。这个一般使用多级查询或者关联子查询才会用到。
2.select_type 列
select_type一般分为六种simple,primary,subquery,derived,union和union result。
simple:简单的select查询,查询中不包含子查询或者union。
primary:查询中包含子查询,最外层的查询呗标记为primary。
subquery:表示在select中包含含子查询,举个例子:explain SELECT id ,(SELECT min(teacher_code) from teacher) as num from student where id <25; 这个查询teacher表的select_type就是subquery;
derived:衍生查询,就是在from 后面的子查询,MySQL会将结果放在一个额临时表中,也称为派生表。举栗子:explain SELECT * from (SELECT teacher_code from teacher where school_id >10 ) a
不过大家记得如果是MySQL 5.7以上版本的话,记得关闭一个东东,就是执行这个语句:set session optimizer_switch='derived_merge=off';
,因为5.7以上对衍生表进行了合并优化。
union:表示union中的第二个或后面的select语句。
union result:union的结果。
3.table 列
表示这个SQL语句会访问哪张表。
4.type 列
这一列是我们最常用也是和优化SQL关系最大的一列,决定这这个SQL语句在MySQL中如何运行,是否使用索引。依次从最优到最差的级别为:
system>const>eq_ref>ref>range>index>ALL 。通常情况下,需要达到range级别,越往上越好。
system:表只有一行记录,这是const类型的特例。
const:通过索引一次就能找到,一般用于主键索引(一般是id)或者是唯一索引(unique index),只匹配一条数据,读取1次,所以效率特别快。SQL如下:
eq_ref: 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引扫描。
ref:使用普通索引或者是唯一索引的部分前缀(like 也可能使用索引),索引与某个值比较,可能会找到多个符合条件的数据。

5. possible_keys 列
这一列显示查询可能使用哪些索引来查找。explain是可能出现possible_key有列,而key显示为NULL 的情况,这种情况因为表中数据不多,MySQL认为索引对此查询帮助不大,所以选择了全表查询。
如果该列是null,则没有相关索引,这种情况下,可通过检查where子查询看是否可以创造一个适当的索引来提高查询性能,然后用explain查看效果。
6.key列
这一列显示mysql实际采用哪个索引来优化该表的访问。
如果没有使用索引,则该列是null,如果想轻质mysql使用或者忽视possible_keys列中的索引,在查询中使用ignore index,force index。
7.ken_lenlie
这个是索引使用的字节数,通过这个值可以计算出使用了索引中的哪些列。索引的最大长度是768个字节,如果字符串过长,mysql会做一个类似左前缀索引的处理,并将前半部分的字符提取出来做索引。
8.ref 列
这一列显示了在key列记录的索引中,查找到值所用的列或常量,常见的有:const,字段名,
9.row列
这一列是mysq估计要读取并检测的行数,但是结果并不一定是这个行数。
这篇拖延了好久了了,先这样,后面的下面再补充【捂脸】。
no sacrifice,no victory~