索引優化實戰
- 1、 全值匹配
- 2、 最佳左字首法則:如果索引多列,要遵守最左字首法則。指的是查詢從索引的最左錢磊開始且不跳過索引中的列
- 3、 不在索引上做任何操作(計算,函式,型別轉換),會導致索引失效
- 4、 儲存引擎不能使用索引中範圍條件右邊的列
- 5、 儘量使用覆蓋索引(只訪問索引的查詢(索引列和查詢列一致)),減少select *
- 6、 Mysql在使用不等於(!=或者<>)的時候無法使用索引會導致全表掃描
- 7、 Is null 和is not null 也無法使用索引
- 8、 Like以萬用字元開頭(’%abc…’)mysql索引會失效
- 9、 字串不加單引號索引會失效
- 10、 少用or,用它來連線時索引會失效
針對上面的十種情況,實戰操作一下。
建表語句:
CREATE TABLE `student` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`stuCode` varchar(255) NOT NULL COMMENT '學號',
`stuName` varchar(255) NOT NULL COMMENT '姓名',
`class` varchar(255) DEFAULT NULL COMMENT '班級',
`hobby` varchar(255) DEFAULT NULL COMMENT '愛好' ,
PRIMARY KEY (`sid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO `Student` (`sid`, `stuCode`, `stuName`, `class`, `hobby`) VALUES ('1', '10001', 'zhangsan', '1', '2');
INSERT INTO ` Student` (`sid`, `stuCode`, `stuName`, `class`, `hobby`) VALUES ('2', '10002', 'wangwu', '2', NULL) ;
INSERT INTO `Student` (`sid`, `stuCode`, `stuName`, `class`, `hobby`) VALUES ('3', '10003', 'xiaohong', '2', '1');
INSERT INTO `Student` (`sid`, `stuCode`, `stuName`, `class`, `hobby`) VALUES ('4', '10004', 'zhaosi', '3', NULL);
建立聯合索引stuCodeNameClass
ALTER TABLE `Student` ADD INDEX stuCodeNameClass ( `stuCode`,`stuName`,`class` )
檢視索引
show INDEX from student
這裡就可以看到我們建立的聯合索引(主鍵會自動建立索引)
1、 全值匹配
explain select * from student where stuCode='10001' and stuName = 'zhangsan' and class = '2'
從結果中我可以看出我們確實走了stuCodeNameClass索引,從ref的三個const看,我們建立的聯合索引,三個欄位都用到了,這種匹配效率是最好的(注意一下此時的key_len是2302)
現在我們去掉class條件,看看會怎麼樣
explain select * from student where stuCode='10001' and stuName = 'zhangsan'
這個可以看出我們沒有走全部的欄位,雖然這種也走了索引,但是效率沒有全值匹配好
2、 最佳左字首法則:如果索引多列,要遵守最左字首法則。指的是查詢從索引的最左前列開始且不跳過索引中的列
針對上面那個查詢語句,我們去掉了class,依然走了索引,現在我們把stuName也去掉,看看效果怎樣
explain select * from student where stuCode='10001'
不難看出,只有stuCode依然會走索引。那現在我們去掉stuCode,只用stuName和class
explain select * from student where stuName = 'zhangsan' and class = '2'
Type為All,並沒有走索引,因為該語句違背了最佳左綴法則,查詢從最左前列開始。
現在我們用stuCode和class來查詢,看看效果如何
explain select * from student where stuCode='10001' and class = '2'
從結果中看,只用了stuCode一個欄位,違背不跳過索引中的列
3、 不在索引上做任何操作(計算,函式,型別轉換),會導致索引失效
當我們只使用stuCode作為條件時,會走索引,如下
explain select * from student where stuCode='10001'
如果我們在條件stuCode上使用left函式(從左擷取)會怎樣
explain select * from student where LEFT(stuCode,7) ='10001'
Type為All,沒有走索引。
4、 儲存引擎不能使用索引中範圍條件右邊的列(簡單點來說就是條件範圍後面的索引會失效)
現在我們在建一個teacher表
CREATE TABLE `teacher` (
`tid` int(11) NOT NULL,
`number` int(11) DEFAULT NULL,
`tCode` varchar(255) DEFAULT NULL,
`tName` varchar(255) DEFAULT NULL,
PRIMARY KEY (`tid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `teacher` (`tid`, `number`, `tCode`, `tName`) VALUES ('1', '1', '20001', 'wang');
INSERT INTO `teacher` (`tid`, `number`, `tCode`, `tName`) VALUES ('2', '2', '20001', 'yang');
建立索引numCode
ALTER TABLE `teacher` ADD INDEX numCode ( `number`,`tCode`)
按索引查詢一下teacher
explain select * from teacher where number =1 and tCode = '20001'
正常走兩個索引,注意key_len是773,現在稍微改一下查詢語句
explain select * from teacher where number >1 and tCode = '20001'
Type是range我們從key可以看出我們還是走了索引,但是注意key_len可知我們只走了number索引,後面的tCode沒有使用。
**5、 儘量使用覆蓋索引(只訪問索引的查詢(索引列和查詢列一致)),減少select ***
現在還是使用student表
explain select * from student where stuCode = '10001' and stuName='zhangsan' and class = '1'
現在修改一下查詢語法
explain select stuCode,stuName,class from student where stuCode = '10001' and stuName='zhangsan' and class = '1'
跟上面的區別在於Extra中有一個Using index,這表示我們掃描了索引就可以獲得結果,不用再去掃描表,效果好
6、 Mysql在使用不等於(!=或者<>)的時候無法使用索引會導致全表掃描
explain select * from student where stuCode != '10001'
7、 is null和is not null 也無法使用索引
explain select * from student where stuCode is not null
8、 like以萬用字元開頭(’%abc…’)mysql索引會失效
建立user表
CREATE TABLE `user` (
`uid` int(11) NOT NULL,
`uname` varchar(255) DEFAULT NULL,
`uage` int(11) DEFAULT NULL,
`usex` int(11) DEFAULT NULL,
PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `user` (`uid`, `uname`, `uage`, `usex`) VALUES ('1', '1abc1', '1', NULL);
INSERT INTO `user` (`uid`, `uname`, `uage`, `usex`) VALUES ('2', '2abc2', '2', NULL);
INSERT INTO `user` (`uid`, `uname`, `uage`, `usex`) VALUES ('3', '3abc3', '3', NULL);
建立索引
ALTER TABLE `user` ADD INDEX nameAge ( `uname`,`uage`)
嘗試使用索引查詢
explain select * from user where uname like '%abc%'
但是隻有右邊有%的時候,是可以使用索引的
explain select * from user where uname like 'abc%'
但是有大部分需求是要求前後模糊查詢的,這個時候有什麼解決辦法呢,這個其實可以跟第五條結合
explain select uid,uname,uage from user where uname like '%abc%'
9、 字串不加單引號索引會失效
使用student表
explain select * from student where stuCode = 10001
10、 少用or,用它來連線時索引會失效
explain select * from student where stuCode = '10001' or stuName = 'zhangsan'