-- 示例表 CREATE TABLE `employees` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名', `age` int(20) NOT NULL DEFAULT '0' COMMENT '年龄', `position` varchar(20) NOT NULL DEFAULT '' COMMENT '职位', `hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '入职时间', PRIMARY KEY (`id`), KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE, KEY `idx_age` (`age`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=136326 DEFAULT CHARSET=utf8 COMMENT='员工表' --建立100000条记录 drop procedure if EXISTS insert_emp; delimiter ;; create procedure insert_emp() BEGIN declare i int; set i=1; while(i < 100000)DO INSERT INTO employees(name,age,position) values(CONCAT('xiaoqiang',i),i,'coder'); SET i=i+1; end WHILE; end;; delimiter ; call insert_emp();
select * from employees LIMIT 9999 ,5;
表示从表employees 中取出从10000行开始的5行记录。看似只查询5条记录,实际这条SQL是先读取10005条记录,而后抛弃前10000条记录,而后读到后面5条想要的数据。没有添加单独的order by,表示经过主键排序。
所以要查询一张大表比较靠后的数据,执行效率是很是低的。
由于主键是自增且连续的,因此能够改写成按照主键查询从第10001开始的五行数据,以下:web
select * from employees WHERE id > 9999 limit 5;
能够看到两个sql的执行计划,显然改写后的sql走了索引,并且扫描的行数大大减小,执行效率会更高。可是,这条改写的sql在不少场景下并不实用,由于表中可能某些记录被删除后,主键空缺,致使结果不一致。
先删除一条记录,而后测试下原来sql和优化后的sql:sql
select * from employees LIMIT 9999 ,5;
select * from employees where id> 9999 limit 5;
两条sql的结果不同,所以,若是主键不连续,不能使用上面描述的方法。
另外因为原来sql是order by非主键字段,按照上面的方法改写sql的结果不一致。因此这种改写得知足如下两个条件:编程
结果是按照主键排序的后端
select * from employees order by name limit 9000, 5;
explain select * from employees order by name limit 9000, 5;
key字段对应的值为null,发现并无使用name字段的索引。由于扫描整个索引并查找到没有索引的行,可能要便利多个索引树,其成本比扫描全表的成本更高,索引优化器放弃使用索引。
优化的关键是:让排序时返回的字段尽量的少,因此可让排序和分页操做先查出主键,而后根据主键查到对应的记录。
改下以下:机器学习
select * from employees as e inner join(select id from employees order by name limit 9000,5) as ed on e.id=ed.id;
能够看到结果与原来的sql结果是一致的,执行时间减小了通常以上,再对比下执行计划:
原来的sql使用的是filesort排序,而优化后的sql使用的是索引排序。编程语言
原则:小表驱动大表,即小表的数据集驱动大表的数据集
in:当B表的数据集小于A表的数据集时,in因为exists工具
select * from A where id in(select id from B) 等价于 for(select id from B){ select * from A where A.id=B.id }
exists:当A表的数据集小于B表的数据集时,exitsts优于in
当著查询A的数据,放到子查询B中作条件验证,根据验证结果(true或false)来决定著查询的数据是否保留。性能
select * from A exists(select 1 from B where A.id=B.id) 等价于 for(select * from A){ select * from B where A.id=B.id }
explain select count(1) from employees; explain select count(id) from employees; explain select count(name) from employees; explain select count(*) from employees;
四个sql的执行计划几乎同样的,count(name)使用的是联合索引, 主要区别根据某个字段作count操做不会统计字段为null的值的数据行。
除了count(name)的其余count操做,都是用的辅助索引而不是主键索引, 由于二级索引存储数据更少,检索性能更高。学习
还没关注个人公众号?测试