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)