索引的规则:
1.索引失效情况
<1>全值匹配我最爱
建立索引提高效率 CREATE INDEX 索引名字 ON 表名(表字段,表字段,表字段,......)
建立索引前:
索引后:
<2>最佳左前缀法则
如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。
EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 AND emp.name = 'abcd'
虽然只是使用了一部分但是效率还是提高了。
完全没有使用上索引
结论:过滤条件要使用索引必须按照索引建立时的顺序,依次满足,一旦跳过某个字段,索引后面的字段都无法被使用。
<3>不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
这两条sql哪种写法更好
SELECT SQL_NO_CACHE * FROM emp WHERE emp.name LIKE 'abc%'
SELECT SQL_NO_CACHE * FROM emp WHERE LEFT(emp.name,3) = 'abc'
第一种:
第二种:
<4>存储引擎不能使用索引中范围条件右边的列,可以将范围索引字段放置到索引中的最右位置
如果系统经常出现的sql如下:
SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 AND emp.deptId>20 AND emp.name = 'abc' ;
那么索引 idx_age_deptid_name这个索引还能正常使用么?
如果这种sql 出现较多。应该建立: create index idx_age_name_deptid on emp(age,name,deptid)
效果:
<5>mysql 在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描
<6>is not null 也无法使用索引,但是is null是可以使用索引的
<7>like以通配符开头('%abc...')mysql索引失效会变成全表扫描的操作
<8>字符串不加单引号索引失效
总结:
2.建议
单表查询优化:
当范围条件和group by 或者 order by 的字段出现二选一时 ,优先观察条件字段的过滤数量,如果过滤的数据足够多,而需要排序的数据并不多时,优先把索引放在范围字段上。反之,亦然。
关联查询优化:如 left join /right join/union等
建议
子查询优化:
尽量不要使用not in 或者 not exists 用left outer join on xxx is null 替代
如:
SELECT SQL_NO_CACHE * FROM emp a WHERE id NOT IN(SELECT ceo FROM dept b2
WHERE ceo IS NOT NULL) group by age having count(*)<10000
修改:
EXPLAIN SELECT SQL_NO_CACHE a.* FROM emp a LEFT OUTER JOIN dept b
ON a.id =b.ceo WHERE b.ceo IS NULL
group by age having count(*)<10000
order by关键字优化
CREATE TABLE `emp` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) DEFAULT NULL,
`age` INT(3) DEFAULT NULL,
`deptid` INT(11) DEFAULT NULL,
empno int not null,
PRIMARY KEY (`id`),
KEY `fk_dept_id` (`deptId`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
create index idx_age_deptid_name on emp (age,deptid,name) -- 创建索引
drop index idx_age_deptid_name on emp -- 删除索引
以下 是否能使用到索引,能否去掉using filesort
1、explain select * from emp order by age,deptid; -- Using filesort
不能 没有过滤条件
2、 explain select * from emp order by age,deptid limit 10; -- Using filesort
#若无过滤排序折
3、 explain select * from emp where age=45 order by deptid; -- Using where
4、explain select * from emp where age=45 order by deptid,name; -- Using where
5、explain select * from emp where age=45 order by deptid,empno;-- Using where; Using filesort
6、explain select * from emp where age=45 order by name,deptid;-- Using where; Using filesort
7、 explain select * from emp where deptid=45 order by age; -- Using where; Using filesort
#顺序不对排序折
8、explain select * from emp where age=45 order by age,deptid; -- Using where
9、explain select * from emp where age=45 order by deptid,age; -- Using where
#值确定无须排序
10、 EXPLAIN SELECT * FROM emp WHERE age=50 AND deptid >8000 ORDER BY NAME; -- Using index condition; Using where; Using filesort
11、 EXPLAIN SELECT * FROM emp WHERE age=50 AND deptid >8000 ORDER BY deptid, NAME; -- Using index condition; Using where
#谁残还得谁来续
12、 explain select * from emp where age=45 order by deptid desc, name desc ; -- Using where
13、 explain select * from emp where age=45 order by deptid asc, name desc ;-- Using where; Using filesort
#南辕北辙排序折(排序顺序不一致)
1.双路排序
MySQL 4.1之前是使用双路排序,字面意思就是两次扫描磁盘,最终得到数据, 读取行指针和orderby列,对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出。从磁盘取排序字段,在buffer进行排序,再从磁盘取其他字段。取一批数据,要对磁盘进行了两次扫描,众所周知,I\O是很耗时的,所以在mysql4.1之后,出现了第二种改进的算法,就是单路排序。
3.单路排序
从磁盘读取查询需要的所有列,按照order by列在buffer对它们进行排序,然后扫描排序后的列表进行输出, 它的效率更快一些,避免了第二次读取数据。并且把随机IO变成了顺序IO,但是它会使用更多的空间, 因为它把每一行都保存在内存中了。
结论及引申出的问题
由于单路是后出的,总体而言好过双路, 但是用单路有问题 :在sort_buffer中,方法B比方法A要多占用很多空间,因为方法B是把所有字段都取出, 所以有可能取出的数据的总大小超出了sort_buffer的容量,导致每次只能取sort_buffer容量大小的数据,进行排序(创建tmp文件,多路合并),排完再取取sort_buffer容量大小,再排……从而多次I/O。本来想省一次I/O操作,反而导致了大量的I/O操作,反而得不偿失。
4.优化策略
增大sort_buffer_size参数的设置
增大max_length_for_sort_data参数的设置
减少select 后面的查询的字段。
提高Order By的速度
1. Order by时select * 是一个大忌只Query需要的字段, 这点非常重要。在这里的影响是:
1.1 当Query的字段大小总和小于max_length_for_sort_data 而且排序字段不是 TEXT|BLOB 类型时,会用改进后的算法——单路排序, 否则用老算法——多路排序。
1.2 两种算法的数据都有可能超出sort_buffer的容量,超出之后,会创建tmp文件进行合并排序,导致多次I/O,但是用单路排序算法的风险会更大一些,所以要提高sort_buffer_size。
2. 尝试提高 sort_buffer_size
不管用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为这个参数是针对每个进程的 1M-8M之间调整
3. 尝试提高 max_length_for_sort_data
提高这个参数, 会增加用改进算法的概率。但是如果设的太高,数据总容量超出sort_buffer_size的概率就增大,明显症状是高的磁盘I/O活动和低的处理器使用率. 1024-8096之间调整
GROUP BY关键字优化
分页查询的优化---limit
EXPLAIN SELECT SQL_NO_CACHE * FROM emp ORDER BY deptid LIMIT 10000,40
给deptno这个字段加上索引后:
没什么用。因为优化器认为limit的过滤效果并不好,所以放弃了使用索引过滤。
优化: 先利用覆盖索引把要取的数据行的主键取到,然后再用这个主键列与数据表做关联:
EXPLAIN SELECT SQL_NO_CACHE * FROM emp INNER JOIN (SELECT id FROM emp e ORDER BY deptno LIMIT 10000,40) a ON a.id=emp.id
覆盖索引 --> 就是,select 到 from 之间查询的列 <=使用的索引列+主键