MySQL索引優化與分析(重要)

建表SQL

CREATE TABLE staffs (
  id INT PRIMARY KEY AUTO_INCREMENT,
  NAME VARCHAR (24)  NULL DEFAULT '' COMMENT '姓名',
  age INT NOT NULL DEFAULT 0 COMMENT '年齡',
  pos VARCHAR (20) NOT NULL DEFAULT '' COMMENT '職位',
  add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入職時間'
) CHARSET utf8 COMMENT '員工記錄表' ;
 
 
INSERT INTO staffs(NAME,age,pos,add_time) VALUES('z3',22,'manager',NOW());
INSERT INTO staffs(NAME,age,pos,add_time) VALUES('July',23,'dev',NOW());
INSERT INTO staffs(NAME,age,pos,add_time) VALUES('2000',23,'dev',NOW());
INSERT INTO staffs(NAME,age,pos,add_time) VALUES(null,23,'dev',NOW());
SELECT * FROM staffs;
 
ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos(name, age, pos);

案例(索引失效)

1、全值匹配我最愛

索引  idx_staffs_nameAgePos 建立索引時 以 name , age ,pos 的順序建立的。全值匹配表示 按順序匹配的
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July';
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25;
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25 AND pos = 'dev';

說明:如果查詢過程中,若沒有查詢第一個NAME字段,索引將無效

有NAME,無age,部分索引失效

第一個索引字段相當於火車頭,後面的索引字段相當於車廂,如果只剩下車頭,火車依然可以開動,若沒有車頭,火車將無法運行。可以以此進行類比。

2、最佳左前綴法則

如果索引了多列,要遵守最左前綴法則。指的是查詢從索引的最左前列開始並且不跳過索引中的列

 

and 忽略左右關係。既即使沒有沒有按順序 由於優化器的存在,會自動優化。
經過試驗結論  建立了 idx_nameAge 索引  id 爲主鍵
    1.當使用覆蓋索引的方式時,(select name/age/id from staffs where age=10 (後面沒有其他沒有索引的字段條件)),即使不是以 name 開頭,也會使用 idx_nameAge 索引。
    既 select 後的字段 有索引,where 後的字段也有索引,則無關執行順序。
    2.除開上述條件 才滿足最左前綴法則。
 
EXPLAIN SELECT * FROM staffs WHERE age = 25 AND pos = 'dev';
 
EXPLAIN SELECT * FROM staffs WHERE pos = 'dev';

口訣:帶頭大哥不能死,中間兄弟不能斷。百分like加右邊

3、不要在索引列上做任何操作(計算、函數、(自動or手動)類型轉換),會導致索引失效而轉向全表掃描

EXPLAIN SELECT * FROM staffs WHERE left(NAME,4) = 'July';

4、存儲引擎不能使用索引中範圍條件右邊的列

範圍 若有索引則能使用到索引,範圍條件右邊的索引會失效(範圍條件右邊與範圍條件使用的同一個組合索引,右邊的纔會失效。若是不同索引則不會失效)

5、儘量使用覆蓋索引(只訪問索引的查詢(索引列和查詢列一致)),減少select *

6、mysql 在使用不等於(!= 或者<>)的時候無法使用索引會導致全表掃描

索引  idx_nameAgeJob
         idx_name
使用 != 和 <> 的字段索引失效( != 針對數值類型。 <> 針對字符類型
前提 where and 後的字段在混合索引中的位置比比當前字段靠後  where age != 10 and name='xxx'  ,這種情況下,mysql自動優化,將 name='xxx' 放在 age !=10 之前,name 依然能使用索引。只是 age 的索引失效)

7、is not null 也無法使用索引,但是is null是可以使用索引的

8、like以通配符開頭('%abc...')mysql索引失效會變成全表掃描的操作

like ‘%abc%’ type 類型會變成 all
like ‘abc%’ type 類型爲 range ,算是範圍,可以使用索引

問題:解決like '%字符串%'時索引不被使用的方法??

建表,插入數據

CREATE TABLE `tbl_user` (
 `id` INT(11) NOT NULL AUTO_INCREMENT,
 `NAME` VARCHAR(20) DEFAULT NULL,
 `age` INT(11) DEFAULT NULL,
 email VARCHAR(20) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
 
#drop table tbl_user
 
INSERT INTO tbl_user(NAME,age,email) VALUES('1aa1',21,'[email protected]');
INSERT INTO tbl_user(NAME,age,email) VALUES('2aa2',222,'[email protected]');
INSERT INTO tbl_user(NAME,age,email) VALUES('3aa3',265,'[email protected]');
INSERT INTO tbl_user(NAME,age,email) VALUES('4aa4',21,'[email protected]');
INSERT INTO tbl_user(NAME,age,email) VALUES('aa',121,'[email protected]');

建索引之前進行測試

#before index
 
EXPLAIN SELECT NAME,age    FROM tbl_user WHERE NAME LIKE '%aa%';
 
EXPLAIN SELECT id    FROM tbl_user WHERE NAME LIKE '%aa%';
EXPLAIN SELECT NAME     FROM tbl_user WHERE NAME LIKE '%aa%';
EXPLAIN SELECT age   FROM tbl_user WHERE NAME LIKE '%aa%';
 
EXPLAIN SELECT id,NAME    FROM tbl_user WHERE NAME LIKE '%aa%';
EXPLAIN SELECT id,NAME,age FROM tbl_user WHERE NAME LIKE '%aa%';
EXPLAIN SELECT NAME,age FROM tbl_user WHERE NAME LIKE '%aa%';
 
EXPLAIN SELECT *     FROM tbl_user WHERE NAME LIKE '%aa%';
EXPLAIN SELECT id,NAME,age,email  FROM tbl_user WHERE NAME LIKE '%aa%';

需求分析:查詢時字段兩邊都是%,且索引不能失效

創建索引(覆蓋索引)

#create index
CREATE INDEX idx_user_nameAge ON tbl_user(NAME,age);
 
#DROP INDEX idx_user_nameAge ON tbl_user

分析:email字段並沒有創建索引,覆蓋索引失效

覆蓋索引:建立的索引字段和查詢的字段在個數、順序上最好完全一致。

 9、字符串不加單引號索引失效

 底層進行轉換使索引失效,使用了函數造成索引失效

10、少用or,用它來連接時會索引失效

【總結】

帶頭大哥不能死

中間兄弟不能斷(永遠要符合最佳左前綴原則)

索引列上無計算

like百分加右邊

範圍之後全失效

字符串裏有引號