1)建表语句 students表,并插入3条语句mysql
CREATE TABLE `students` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(64) DEFAULT NULL, `sex` varchar(64) DEFAULT NULL, `age` int(11) DEFAULT NULL, `city` varchar(64) DEFAULT NULL, `birthday` datetime DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `students_id_uindex` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
INSERT INTO students (name, sex, age, city, birthday) VALUES ('tom', 'm', 12, '北京', '2007-10-19 08:46:47');
INSERT INTO students (name, sex, age, city, birthday) VALUES ('alice', 'w', 13, '上海', '2006-10-19 08:48:06');
INSERT INTO students (name, sex, age, city, birthday) VALUES ('join', 'm', 15, '上海', '2004-10-19 08:48:06');
2)在未建索引时执行下面的sql语句,extra中出现了Using filesort,会影响性能,须要优化的sql
3)数据库
优化1:name、age、birthdy建复合索引 ,而后再执行sql语句分析函数
mysql> create INDEX idx_students_na_ag_bir ON students(name,age,birthday); Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0
理论上用到索引,实际也用到了索引,可是ref为null且extra中仍然有Using filesort,因此索引失效了;oop
缘由:由于mysql的索引是BTree索引的工做原理,此sql会先排序name,若是遇到相同的name,则再排序age,age相同则再排序birthdy; 当age处于中间位置时,且age是用的“>”即范围查询时,mysql没法利用索引再对后面的birthdy进行检索(即range类型查询字段后面的索引无效),从而致使索引失效性能
优化2:根据失效缘由从新建索引(索引中去掉age)测试
mysql> drop INDEX idx_students_na_ag_bir ON students; Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> create INDEX idx_students_na_bir ON students(name,birthday); Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0
从新建索引后,ref为const用到了索引,且extra没有了Using filesort优化
students表和city表 ui
CREATE TABLE `city` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(64) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `city_id_uindex` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; INSERT INTO city (name) VALUES ('北京'); INSERT INTO city (name) VALUES ('上海'); INSERT INTO city (name) VALUES ('南京');
1)未建索引的状况下spa
EXPLAIN SELECT * FROM students LEFT JOIN city ON students.city=city.name;
2)在students表建索引
create INDEX in_city ON students(city);
EXPLAIN SELECT * FROM students LEFT JOIN city ON students.city=city.name; //加上索引后,并未起做用
3)在city表加索引
drop INDEX in_city ON students;
create INDEX in_city ON city(name);
EXPLAIN SELECT * FROM students LEFT JOIN city ON students.city=city.name; //city的type为ref,ref也有值,说明加索引已生效
为何在left join的左表加索引不起做用呢?
这是因为左链接持续性致使的,左边表的数据都有,left join 用于有右边搜索行,因此索引应建在left join 右边的表;同理,right join 应建在左边的表
join语句的优化:
1. 尽量减小Join 语句中的Nested Loop 的循环总次数;
如何减小Nested Loop 的循环总次数?最有效的办法只有一个,那就是让驱动表的结果集尽量的小。
为何?由于驱动结果集越大,意味着须要循环的次数越多,也就是说在被驱动结果集上面所须要执行的查询检索次数会越多。好比,当两个表(表A 和表B) Join 的时候,若是表A 经过WHERE 条件过滤后有10 条记录,而表B 有20 条记录。若是咱们选择表A 做为驱动表,也就是被驱动表的结果集为20,那么咱们经过Join 条件对被驱动表(表B)的比较过滤就会有10 次。反之,若是咱们选择表B 做为驱动表,则须要有20 次对表A 的比较过滤。固然,此优化的前提条件是经过Join 条件对各个表的每次访问的资源消耗差异不是太大。若是访问存在较大的差异的时候(通常都是由于索引的区别),咱们就不能简单的经过结果集的大小来判断须要Join 语句的驱动顺序,而是要经过比较循环次数和每次循环所须要的消耗的乘积的大小来获得如何驱动更优化。
2. 优先优化Nested Loop 的内层循环;
不只仅是在数据库的Join 中应该作的,实际上在咱们优化程序语言的时候也有相似的优化原则。内层循环是循环中执行次数最多的,每次循环节约很小的资源,在整个循环中就能节约很大的资源。
3. 保证Join 语句中被驱动表上Join 条件字段已经被索引;
保证被驱动表上Join 条件字段已经被索引的目的,正是针对上面两点的考虑,只有让被驱动表的Join 条件字段被索引了,才能保证循环中每次查询都可以消耗较少的资源,这也正是优化内层循环的实际优化方法。
4. 当没法保证被驱动表的Join 条件字段被索引且内存资源充足的前提下,不要太吝惜Join
Buffer 的设置:
当在某些特殊的环境中,咱们的Join 必须是All,Index,range 或者是index_merge 类型的时候,Join Buffer 就会派上用场了。在这种状况下,Join Buffer 的大小将对整个Join 语句的消耗起到很是关键的做用。
1.全值匹配我最爱
2.最佳左前缀法则 (若是索引了多列,要遵照最左前缀法则,指的是查询从索引的最前列开始,而且不跳过索引中的列)
3.不在索引上作任何操做(计算、函数、类型转换 会致使索引失效,从而致使全表扫描)
4.存储引擎不能使用索引中范围右边的列
5.尽可能使用覆盖索引(只访问索引的查询(索引列和查询列一致)),尽可能不使用select *
6.mysql在使用不等于(!=或<>)时没法使用索引,从而致使全表扫描
7. is not null 也没法使用索引
8. like以通配符开头(%abc...),索引会失效,从而致使全表扫描
9.字符串不加单引号索引失效
10.少用or,用它作链接时索引会失效
-1)全值匹配我最爱
create INDEX index_name_sex_age ON students(name, sex, age) ;
EXPLAIN SELECT * FROM students WHERE name='tom' AND sex='m' AND age=12; //建的索引和用到的索引,所有匹配,且顺序一致
-2)最佳左前缀法则 (若是索引了多列,要遵照最左前缀法则,指的是查询从索引的最前列开始,而且不跳过索引中的列)
1.EXPLAIN SELECT * FROM students WHERE name='tom'; //匹配到了复合索引的第一个字段name 因此用到了索引,ref为const
2.EXPLAIN SELECT * FROM students WHERE sex='m' AND age=12; //由于从复合索引的第二个字段开始,跳过了第一个致使索引失效,引发全表扫描
3.EXPLAIN SELECT * FROM students WHERE name='tom' AND age=12; //虽然用到了索引,但只用到了name ,因为跳过了sex致使age未用到索引,故ref中只有一个const
-3)不在索引上作任何操做(计算、函数、类型转换 会致使索引失效,从而致使全表扫描)
1. EXPLAIN SELECT * FROM students WHERE name='tom';
2. EXPLAIN SELECT * FROM students WHERE left(name,4)='tom'; //使用了left函数后,索引失效
-4)存储引擎不能使用索引中范围右边的列
EXPLAIN SELECT * FROM students WHERE name='tom' AND sex='m' AND age=12;
EXPLAIN SELECT * FROM students WHERE name='tom' AND sex='m' AND age>12; //使用范围时,索引失效
-5) 尽可能使用覆盖索引(只访问索引的查询(索引列和查询列一致)),尽可能不使用select *
EXPLAIN SELECT name ,sex,age FROM students WHERE name='tom' AND sex='m' AND age>12;
由4的例子能够看到使用“>”时,ref为null,在5中select * 换位索引列后 SELECT name ,sex,age,ref为const,extra中也用上“using index” 因此要尽可能查询索引列
-6).mysql在使用不等于(!=或<>)时没法使用索引,从而致使全表扫描
EXPLAIN SELECT * FROM students WHERE name!='tom' ; //!= 致使索引失效
-7). is not null 也没法使用索引
EXPLAIN SELECT * FROM students WHERE name is NOT NULL ;
-8). like以通配符开头(%abc...),索引会失效,从而致使全表扫描
由下图可知,当select * 时,通配符%在右边的话,致使索引失效
可是由于业务须要非得用“%...%”匹配的话,能够在select时使用覆盖索引,以免索引失效
由下图测试可知,当查询的列不在索引中,也会致使索引失效
-9).字符串不加单引号索引失效
以下图所示,未使用单引号的索引失效
- 10).少用or,用它作链接时索引会失效
使用or时 type为All索引失效
mysql> EXPLAIN SELECT * FROM students WHERE name ='tom' OR name='join';
+----+-------------+----------+------------+------+--------------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+--------------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | students | NULL | ALL | index_name_sex_age | NULL | NULL | NULL | 1 | 100.00 | Using where |
+----+-------------+----------+------------+------+--------------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)