性能分析
MySql Query Optimizer
自身select語句優化模塊(未必是DBA認爲最優的)
mysql常見瓶頸
CPU:CPU在飽和的時候通常發生在數據裝入內存或從磁盤上讀取數據的時候
IO:磁盤IO瓶頸發生在裝入數據遠大於內存容量的時候
服務器硬件性能瓶頸
Explain
模擬優化器執行sql語句,從而知道mysql是如何處理sql語句的,從而分析出性能瓶頸node
MyISAM 不支持外鍵 不支持事務 表鎖 只緩存索引,不緩存數據 表空間小 關注性能
InnoDB 支持外鍵 支持事務 行鎖 緩存索引及數據 表空間大 關注事務mysql
Percona的xtradb引擎是一款能夠替代InnoDB的引擎,在性能和併發上作的更好算法
多表查詢sql
select <select_list> from tableA A left join tableB B on A.key=B.key 右補NULL select <select_list> from tableA A left join tableB B on A.key=B.key where B.key=NULL A獨有 select <select_list> from tableA A right join tableB B on A.key=B.key 左補NULL select <select_list> from tableA A right join tableB B on A.key=B.key where A.key=NULL B獨有 select <select_list> from tableA A inner join tableB B on A.key=B.key 共有 select <select_list> from tableA A full outer join tableB B on A.key=B.key 全有(mysql不支持,變通爲左聯unin右聯) select <select_list> from tableA A full outer join tableB B on A.key=B.key where A.key=NULL or where B.key=NULL A獨加B獨(mysql不支持,變通爲左獨聯unin右獨聯)
什麼是索引
是幫助Mysql高效獲取數據的數據結構,簡單理解爲「排好序的快速查找數據結構」,即索引兩大功能,排序和查找
數據自己以外,數據庫還維護這一個知足特定查找算法的數據結構,這些數據結構以某種方式指向數據,這樣就能夠在這些數據結構的基礎上實現高級查找算法,這種數據結構就是索引
一般使用BTREE索引(多路搜索樹) ,其餘索引結構有hash。full-text,r-tree數據庫
優勢
提升數據檢索效率,下降數據庫的IO成本
經過索引列對數據進行排序,下降數據排序成本,下降CPU消耗緩存
缺點
索引也是一張表,保存主鍵與索引字段,並指向實體記錄,因此因此列也是要佔用空間
提升了查詢速度,可是會下降更新,即增刪改的速度
索引只是提升效率的一個因素,若是有大數據量的表,須要花時間創建最優索引服務器
delete從邏輯刪除,將記錄標記爲inactive,大量的inactive記錄影響數據查找,穩定後有必要重建索引session
建立索引基本知識
一、索引相似書的目錄,會加快查詢速度
二、在表的列(字段)上建立索引
三、索引會加快查詢速度,可是也會影響更新速度,由於更新會維護索引數據。update修改數據時,會自動更新索引,因此有時會慢
四、索引不是越多越好,通常選擇查詢頻繁的where條件的字段上建立
五、小表或重複值不少的列上通常不建索引,要在大表及重複值少的條件列上建立索引
六、多個列聯合索引有前綴生效特性
七、當字段內容前n個字符已經接近惟一時,能夠對字段前n個字符建立索引
八、索引從工做方式分,有主鍵,惟一,普通索引
九、索引類型有BTREE(默認)和hash(適合緩存,內存數據庫)等數據結構
索引列的建立及生效條件
問題1?既然索引能夠加快查詢速度,那麼就給全部列建索引吧?
解答:索引不但佔用系統空間,更新數據庫時還須要維護索引數據,所以索引是一把雙刃劍,不是越多越好。幾十到幾百行的小表無需創建索引,寫多讀少的業務少建索引。
問題2?到底在哪些列上創建索引呢
secect user,host from mysql.user where host=...索引必定要建立在查詢頻繁條件列,而不是selcect後的選擇數據的列,另外咱們要儘可能選擇在惟一值多的大表上的列創建索引。select count(distinct列) from table併發
適合建立索引的場景
主鍵自動創建惟一索引
頻繁做爲查詢條件的字段應該建立索引
查詢中與其餘表關聯的字段,外鍵關係創建索引
頻繁更新的字段不適合建索引
where條件裏用不到的字段不建索引
單鍵/組合索引的選擇:在高併發下傾向建立組合索引
查詢中排序的字段,排序字段若經過索引去訪問將大大提升排序速度。即創建了inx_col1_col2_col3,order by col,col3優於order by col1
查詢中統計或者分組字段
不適合建立索引的場景
表記錄較少 (300w內)
常常增刪改的表
數據分佈平均的表字段或者重複多。例若有1000條記錄,有999個惟一,索引選擇性是999/1000.越接近於1,效率就越高
索引的創建與刪除
=>增長索引在訪問低谷時進行,幾百萬條須要幾分鐘
=>uniq index惟一索引,(用來約束字段值惟一,好比網站註冊時用的email惟一,查詢速度會快些)
mysql> show index from student; +---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | student | 0 | PRIMARY | 1 | id | A | 4 | NULL | NULL | | BTREE | | | | student | 1 | idx_name | 1 | names | A | 4 | NULL | NULL | YES | BTREE | | | +---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ mysql> drop index idx_name on student; mysql> create index idx_name on student(names); mysql> alter table student drop index idx_name; mysql> alter table student add index idx_name(names);
索引分類
單值
惟一
複合
alter table tb_name add primary key(col_list) 主鍵索引,索引值必須惟一,不能爲NUll
alter table tb_name add unique index_name(col_list) 建立的索引值必須惟一(除了NULL,NULL可能會出現屢次)
alter table tb_name add index_name(col_list) 普通索引,索引值可出現屢次
alter table tb_name add fulltext index_name(col_list) 全文索引
對字段的前n個字符建立索引
mysql> select * from student; +----+--------+-------------+-----+--------+----------+ | id | names | phonenum | age | dept | qq | +----+--------+-------------+-----+--------+----------+ | 1 | 張三 | 13701800003 | 23 | 經理 | 9485754 | | 2 | 李四 | 13701800004 | 24 | 助理 | 12344754 | | 3 | 王五 | 13701800005 | 25 | 員工 | 5414754 | | 4 | 趙六 | 1370180006 | 26 | 員工 | 1209756 | +----+--------+-------------+-----+--------+----------+
mysql> create index idx_qq4 on student(qq(4));
mysql> show index from student\G
*************************** 3. row ***************************
Table: student
Non_unique: 1
Key_name: idx_qq4
Seq_in_index: 1
Column_name: qq
Collation: A
Cardinality: 4
Sub_part: 4
Packed: NULL
Null: YES
Index_type: BTREE
Comment:
Index_comment:
建立聯合索引
/*按照列查詢數據時,聯合索引是有前綴生效特性的
/*index(a,b,c)僅僅a,ab,abc三個查詢條件列能夠走索引,其餘不走索引,因此把常常用於條件查詢的放前面
mysql> create index idx_names_qq on student(names,qq(4)); mysql> show index from student\G *************************** 4. row *************************** Table: student Non_unique: 1 Key_name: idx_names_qq Seq_in_index: 1 Column_name: names Collation: A Cardinality: 4 Sub_part: NULL Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: *************************** 5. row *************************** Table: student Non_unique: 1 Key_name: idx_names_qq Seq_in_index: 2 Column_name: qq Collation: A Cardinality: 4 Sub_part: 4 Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment:
MYSQL查詢優化 explain做用(sql語句執行計劃)
mysql> explain select * from student where qq="5414754"; +----+-------------+---------+------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+---------------+---------+---------+-------+------+-------------+ | 1 | SIMPLE | student | ref | idx_qq4 | idx_qq4 | 13 | const | 1 | Using where | +----+-------------+---------+------+---------------+---------+---------+-------+------+-------------+
id:select
查詢的序列號,包含一組數字,表示查詢中執行select子句或操做表的順序 1、id相同,執行順序由上至下 2、id不一樣,若是是子查詢,id的序號會遞增,越大優先級越高,越先被執行 3、id有相同有不一樣,大的先執行,相同的順序執行 select_type:
主要是用於區別普通查詢、聯合查詢、子查詢或是 SIMPLE 簡單的select查詢,查詢中部包含子查詢或是UNION PRIMARY 查詢中若包含任何複雜查詢的子部分,最外層查詢被標記爲PRIMARY SUBQUERY 在SELECT或WHERE列表中包含了子查詢 DERIVED 在FROM列表中包含的子查詢被標記爲DERIVED(衍生),MYsql會遞歸執行這些子查詢,把結果放在臨時表 UNION 若第二個SELECT出如今UNION後,則標記爲UNION,若UNION包含在FROM子句的子查詢中,外層SELECT將被標記爲DERIVED UNION RESULT 從UNION表獲取結果的SELECT type:
訪問類型排列,顯示了查詢使用了何種類型,經常使用的好到差依次爲system>const>eq_ref>ref>range>index>ALL,#通常來講,得保證查詢至少達到range級別,最好的達到ref system 表只有一行記錄(等於系統表,這是const類型的特列,這個能夠忽略不計) const 表示經過索引一次就查找到,const常見於primary key或者unique索引,由於只有匹配一行數據,因此很快,若是主鍵置於where列表,Mysql就能將該查詢轉換爲一個常量 eq_ref 惟一性索引掃描,對於每一個索引鍵,表中只有一條記錄與之匹配,常見於主鍵或惟一性索引掃描 ref 非惟一性索引掃描,返回匹配某個單獨值得全部行,本質上也是一種索引訪問,它返回全部匹配某個單獨值的行,然而它可能會找到多個符合條件的行,因此他應該屬於查找和掃描的混合體 range 只檢索給定範圍的行,使用一個索引來選擇行,key列顯示使用了哪一個索引,通常就是在where語句中出現了between、<、>、in等條件,這種範圍掃描索引比全表掃描要好,由於它只須要開始於索引的某點,而結束於另外一個點,不用掃描所有索引 index Full Index scan,(全索引掃描)index和ALL的區別爲index類型只遍歷索引樹,一般比ALL快,由於索引文件一般比數據文件小。也就是說雖然all和index都是讀全表,可是index是從索引中讀取,而all則是從硬盤中讀取 all Full Table scan,將遍歷全表以找到匹配的行,全表掃描 possible_keys
顯示可能應用在這張表中的索引,一個或多個,查詢涉及到的字段上若存在索引,則該索引被列出,可是不必定被使用
key
實際使用的索引,若是爲NULL,則沒有使用索引。查詢中若使用到了覆蓋索引(聯合索引個數與select項順序及個數吻合),則該索引僅出如今key列表中
key_len
表示索引中使用的字節數,可經過該列計算查詢中使用的索引長度,在不損失精度的狀況下,長度越短越好,key_len顯示的值爲索引字段的最大可能長度,並不是實際使用長度。即key_len是根據表定義計算而得,不是經過表內檢索出的
ref
顯示索引的哪一列被使用了,若是可能的話,最好是一個常量,哪些列或常量被用於查找索引列上的值
rows
根據表統計信息及索引選用狀況,大體估算出找到所需的記錄所須要讀取的行數 extra
包含在不適合在其餘列中顯示但十分重要的額外信息 Using filesort(差) 說明mysql會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取,mysql中沒法利用索引完成的排序操做稱爲「文件排序」 Using temporary(更差) 使用了臨時表保存中間結果,Mysql在對查詢結果排序時使用了臨時表,常見於排序order by和分組查詢group by Using index(好) 表示相應的select操做中使用到了覆蓋索引(Covering Index),避免訪問了表的數據行,效率不錯 若是同時出現using where,代表索引被用來執行索引鍵值的查找 若是沒有同時出現using where,代表索引被用來讀取數據而非執行查找動做 Covering Index (覆蓋索引):就是select的數據列只用從索引中就能取得,沒必要讀取數據行,mysql能夠利用索引返回select表中的字段,而沒必要根據索引再次讀取數據文件,換句話說,查詢列要被所創建的索引覆蓋。
若是要使用覆蓋索引,必定要注意select取出須要的列,不可select * Using where Using join buffer(不重要,少見) 使用了鏈接緩存 impossible where (不重要,少見) where子句的值老是false,不能用來獲取任何元組(where name=「a」 and name=「b」) select tables optimized away(不重要,少見) 在沒有group by子句的狀況下,基於索引優化min/max操做或者對於myisam存儲引擎優化count(*)操做,沒必要等到執行階段再進行計算,查詢執行計劃生成的階段即完成優化 distinct(不重要,少見) 優化distinct操做,在找到第一匹配的元組後即中止找一樣值得動做
mysql> drop index idx_qq4 on student; mysql> explain select * from student where qq="5414754"; +----+-------------+---------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | student | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+---------+------+---------------+------+---------+------+------+-------------+
索引分析
單表
兩表
三表
單表
#建立測試數據
create table article ( id int(10) unsigned not null primary key auto_increment, auther_id int(10) unsigned not null, category_id int(10) unsigned not null, view int(10) unsigned not null, comments int(10) unsigned not null, title varbinary(255) not null, content text not null ); insert into article values (1,1,1,1,1,"1","1"); insert into article values (2,2,2,2,2,"2","1"); insert into article values (3,3,3,3,3,"3","3");
#查詢category_id=1,且comments>1,view最多的auther_id
mysql> explain select id,auther_id from article where category_id=1 and comments>1 order by view desc limit 1;
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
| 1 | SIMPLE | article | ALL | NULL | NULL | NULL | NULL | 3 | Using where; Using filesort |
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
mysql> create index idx_article_ccv on article(category_id,comments,view);
mysql> explain select id,auther_id from article where category_id=1 and comments>1 order by view desc limit 1; #comments>1範圍之後索引致使失效
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+-----------------------------+
| 1 | SIMPLE | article | range | idx_article_ccv | idx_article_ccv | 8 | NULL | 1 | Using where; Using filesort |
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+-----------------------------+
==>BTREE索引工做原理,A,B,C聯合索引,先排序A,若是有相同的,再排序B,B有相同的,排序C。上述案例由於comments>1條件是個範圍,所以,沒法再利用索引對其後字段進行檢索
mysql> explain select id,auther_id from article where category_id=1 and comments=1 order by view desc limit 1;
+----+-------------+---------+------+-----------------+-----------------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+-----------------+-----------------+---------+-------------+------+-------------+
| 1 | SIMPLE | article | ref | idx_article_ccv | idx_article_ccv | 8 | const,const | 1 | Using where |
+----+-------------+---------+------+-----------------+-----------------+---------+-------------+------+-------------+
mysql> drop index idx_article_ccv on article;
#第一次優化不理想,嘗試第二次
mysql> create index idx_article_cv on article(category_id,view);
mysql> explain select id,auther_id from article where category_id=1 and comments>1 order by view desc limit 1;
+----+-------------+---------+------+----------------+----------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+----------------+----------------+---------+-------+------+-------------+
| 1 | SIMPLE | article | ref | idx_article_cv | idx_article_cv | 4 | const | 1 | Using where |
+----+-------------+---------+------+----------------+----------------+---------+-------+------+-------------+
兩表
create table class ( id int(10) unsigned not null auto_increment, card int(10) unsigned not null, primary key (id) ); create table book ( bookid int(10) unsigned not null auto_increment, card int(10) unsigned not null, primary key (bookid) ); insert into class(card) values (FLOOR(1+(RAND()*20))); insert into class(card) values (FLOOR(1+(RAND()*20))); insert into class(card) values (FLOOR(1+(RAND()*20))); insert into class(card) values (FLOOR(1+(RAND()*20))); insert into class(card) values (FLOOR(1+(RAND()*20))); insert into class(card) values (FLOOR(1+(RAND()*20))); insert into class(card) values (FLOOR(1+(RAND()*20))); insert into class(card) values (FLOOR(1+(RAND()*20))); insert into class(card) values (FLOOR(1+(RAND()*20))); insert into book(card) values (FLOOR(1+(RAND()*20))); insert into book(card) values (FLOOR(1+(RAND()*20))); insert into book(card) values (FLOOR(1+(RAND()*20))); insert into book(card) values (FLOOR(1+(RAND()*20))); insert into book(card) values (FLOOR(1+(RAND()*20))); insert into book(card) values (FLOOR(1+(RAND()*20))); insert into book(card) values (FLOOR(1+(RAND()*20)));
mysql> explain select * from class left join book on class.card=book.card;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | class | ALL | NULL | NULL | NULL | NULL | 10 | |
| 1 | SIMPLE | book | ALL | NULL | NULL | NULL | NULL | 7 | |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
mysql> alter table book add index Y(card);
mysql> explain select * from class left join book on class.card=book.card; #左鏈接,右索引的狀況
+----+-------------+-------+------+---------------+------+---------+-----------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+-----------------+------+-------------+
| 1 | SIMPLE | class | ALL | NULL | NULL | NULL | NULL | 10 | |
| 1 | SIMPLE | book | ref | Y | Y | 4 | gtms.class.card | 1 | Using index |
+----+-------------+-------+------+---------------+------+---------+-----------------+------+-------------+
mysql> drop index Y on book
mysql> alter table class add index Y(card);
mysql> explain select * from class left join book on class.card=book.card; #左鏈接,左索引的狀況
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | class | index | NULL | Y | 4 | NULL | 10 | Using index |
| 1 | SIMPLE | book | ALL | NULL | NULL | NULL | NULL | 7 | |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
==>結論,leftjoin條件用於肯定如何從右表搜索行,左邊必定都有,因此右邊是咱們的關鍵點,必定須要創建索引。
==>即左鏈接,使用右索引。class left join book=book right join class,交換表位置,因此索引不變,可是能夠改變這個
==>右鏈接同理
三表
建立測試數據 create table phone ( phoneid int(10) unsigned not null auto_increment, card int(10) unsigned not null, primary key (phoneid) ) engine=innodb; insert into phone(card) values (FLOOR(1+(RAND()*20))); insert into phone(card) values (FLOOR(1+(RAND()*20))); insert into phone(card) values (FLOOR(1+(RAND()*20))); insert into phone(card) values (FLOOR(1+(RAND()*20))); insert into phone(card) values (FLOOR(1+(RAND()*20))); insert into phone(card) values (FLOOR(1+(RAND()*20))); insert into phone(card) values (FLOOR(1+(RAND()*20))); insert into phone(card) values (FLOOR(1+(RAND()*20))); insert into phone(card) values (FLOOR(1+(RAND()*20))); insert into phone(card) values (FLOOR(1+(RAND()*20))); insert into phone(card) values (FLOOR(1+(RAND()*20))); insert into phone(card) values (FLOOR(1+(RAND()*20)));
mysql> explain select * from class left join book on class.card=book.card left join phone on book.card=phone.card;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | class | ALL | NULL | NULL | NULL | NULL | 10 | |
| 1 | SIMPLE | book | ALL | NULL | NULL | NULL | NULL | 7 | |
| 1 | SIMPLE | phone | ALL | NULL | NULL | NULL | NULL | 12 | |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
在右2表加索引
mysql> alter table phone add index Z(card);
mysql> alter table book add index Y(card);
mysql> explain select * from class left join book on class.card=book.card left join phone on book.card=phone.card;
+----+-------------+-------+------+---------------+------+---------+-----------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+-----------------+------+-------------+
| 1 | SIMPLE | class | ALL | NULL | NULL | NULL | NULL | 10 | |
| 1 | SIMPLE | book | ref | Y | Y | 4 | gtms.class.card | 1 | Using index |
| 1 | SIMPLE | phone | ref | Z | Z | 4 | gtms.book.card | 1 | Using index |
+----+-------------+-------+------+---------------+------+---------+-----------------+------+-------------+
==>後兩行type都是ref且總row優化效果很好,所以索引最好設置在須要常常查詢的字段上
結論jion語句優化
儘量減小join語句中的nestedloop的循環總次數,「永遠用小結果集驅動大的結果集」 即,左鏈接,左邊所有,右邊部分,右邊小,所以索引建在右
優先優化nestedloop的內層循環
保證jion語句中被驅動表上的join條件字段已經被索引
當沒法保證被驅動表的join條件字段被索引且內存資源充足的前提下,不要太吝嗇JoinBuffer的設置
避免索引失效注意點
全值匹配我最愛 最佳左前綴法則 不在索引列上作任何操做(計算、函數、類型轉換(自動或手動)),會致使索引失效而轉向全表掃描 存儲引擎不能使用索引中範圍條件右邊的列 儘可能使用覆蓋索引,減小select * mysql在使用不等於(!=或<>)的時候沒法使用索引會致使全表掃描 is null ,is not null也沒法使用索引 like以通配符開頭 ("%abc") mysql索引失效會變成全表掃描 字符串補加引號索引失效 少用or,用它來鏈接時會索引失效
#建立測試數據
CREATE TABLE `staffs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名', `age` int(11) NOT NULL DEFAULT '0' COMMENT '年齡', `pos` varchar(20) NOT NULL DEFAULT '' COMMENT '職位', `add_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入職時間', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT 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());
alter table staffs add index idx_staffs_nap(name,age,pos);
mysql> explain select * from staffs where name="July"; +----+-------------+--------+------+----------------+----------------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+----------------+----------------+---------+-------+------+-------------+ | 1 | SIMPLE | staffs | ref | idx_staffs_nap | idx_staffs_nap | 74 | const | 1 | Using where | +----+-------------+--------+------+----------------+----------------+---------+-------+------+-------------+ mysql> explain select * from staffs where name="July" and age="25"; +----+-------------+--------+------+----------------+----------------+---------+-------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+----------------+----------------+---------+-------------+------+-------------+ | 1 | SIMPLE | staffs | ref | idx_staffs_nap | idx_staffs_nap | 78 | const,const | 1 | Using where | +----+-------------+--------+------+----------------+----------------+---------+-------------+------+-------------+
mysql> explain select * from staffs where name="July" and age="25" and pos="dev"; +----+-------------+--------+------+----------------+----------------+---------+-------------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+--------+------+----------------+----------------+---------+-------------------+------+-------------+ | 1 | SIMPLE | staffs | ref | idx_staffs_nap | idx_staffs_nap | 140 | const,const,const | 1 | Using where | +----+-------------+--------+------+----------------+----------------+---------+-------------------+------+-------------+
mysql> explain select * from staffs where age="25" and pos="dev";
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
mysql> explain select * from staffs where pos="dev";
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
==>前綴生效原則,即最佳左前綴法則
若是索引了多列,查詢須要從索引的最左前列開始而且不跳過索引中的列
mysql> explain select * from staffs where name="July" and pos="dev";
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-------------+
| 1 | SIMPLE | staffs | ref | idx_staffs_nap | idx_staffs_nap | 74 | const | 1 | Using where |
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-------------+
==>中間跳過了一個,索引部分生效,單獨where name時key_len是74,如今依然是74。建議也別跳過
mysql> explain select * from staffs where left(name,4)="July";
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
==>不在索引列上作任何操做(計算、函數、類型轉換(自動或手動)),會致使索引失效而轉向全表掃描
mysql> explain select * from staffs where name="July" and age>25 and pos="dev";
+----+-------------+--------+-------+----------------+----------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+----------------+----------------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | range | idx_staffs_nap | idx_staffs_nap | 78 | NULL | 1 | Using where |
+----+-------------+--------+-------+----------------+----------------+---------+------+------+-------------+
==>存儲引擎不能使用索引中範圍條件右邊的列,範圍以後全失效(age被用到了,可是用在了排序,而不是查找)
mysql> explain select name,age,pos from staffs where name="July" and age=25 and pos="manager";
+----+-------------+--------+------+----------------+----------------+---------+-------------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+----------------+---------+-------------------+------+--------------------------+
| 1 | SIMPLE | staffs | ref | idx_staffs_nap | idx_staffs_nap | 140 | const,const,const | 1 | Using where; Using index |
+----+-------------+--------+------+----------------+----------------+---------+-------------------+------+--------------------------+
mysql> explain select * from staffs where name="July" and age=25 and pos="manager";
+----+-------------+--------+------+----------------+----------------+---------+-------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+----------------+---------+-------------------+------+-------------+
| 1 | SIMPLE | staffs | ref | idx_staffs_nap | idx_staffs_nap | 140 | const,const,const | 1 | Using where |
+----+-------------+--------+------+----------------+----------------+---------+-------------------+------+-------------+
==>儘可能使用覆蓋索引,減小select *
mysql> explain select * from staffs where name !="July";
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | idx_staffs_nap | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
mysql> explain select * from staffs where name <>"July";
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | idx_staffs_nap | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
==>mysql在使用不等於(!=或<>)的時候沒法使用索引會致使全表掃描
mysql> explain select * from staffs where name is null;
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------+
mysql> explain select * from staffs where name is not null;
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | idx_staffs_nap | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
==>is null ,is not null也沒法使用索引
mysql> explain select * from staffs where name like '%July';
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | NULL | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+------+-------------+
mysql> explain select * from staffs where name like 'July%';
+----+-------------+--------+-------+----------------+----------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+----------------+----------------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | range | idx_staffs_nap | idx_staffs_nap | 74 | NULL | 1 | Using where |
+----+-------------+--------+-------+----------------+----------------+---------+------+------+-------------+
==>like以通配符開頭 ("%abc") mysql索引失效會變成全表掃描
如何解決這種須要使用的狀況呢
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;
insert into tbl_user(name,age,email)values('1aa1','21','b@163.com');
insert into tbl_user(name,age,email)values('2aa2','222','a@163.com');
insert into tbl_user(name,age,email)values('3aa3','265','c@163.com');
insert into tbl_user(name,age,email)values('4aa4','21','d@163.com');
進行以下代碼測試==>經過創建聯合索引,即覆蓋索引來解決
mysql> explain select name,age from tbl_user where name like "%aa%"; +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tbl_user | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ mysql> explain select * from tbl_user where name like "%aa%"; +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tbl_user | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ mysql> explain select name from tbl_user where name like "%aa%"; +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tbl_user | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ mysql> explain select id from tbl_user where name like "%aa%"; +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tbl_user | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ mysql> explain select age from tbl_user where name like "%aa%"; +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tbl_user | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ mysql> explain select id,name,age from tbl_user where name like "%aa%"; +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tbl_user | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ mysql> explain select * from tbl_user where name like "%aa%"; +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tbl_user | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ mysql> create index idx_user_nameAge on tbl_user(name,age); Query OK, 0 rows affected (0.14 sec) ************************************* mysql> explain select name,age from tbl_user where name like "%aa%"; +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | 1 | SIMPLE | tbl_user | index | NULL | idx_user_nameAge | 68 | NULL | 4 | Using where; Using index | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ mysql> explain select id from tbl_user where name like "%aa%"; +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | 1 | SIMPLE | tbl_user | index | NULL | idx_user_nameAge | 68 | NULL | 4 | Using where; Using index | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ mysql> explain select name from tbl_user where name like "%aa%"; +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | 1 | SIMPLE | tbl_user | index | NULL | idx_user_nameAge | 68 | NULL | 4 | Using where; Using index | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ mysql> explain select age from tbl_user where name like "%aa%"; +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | 1 | SIMPLE | tbl_user | index | NULL | idx_user_nameAge | 68 | NULL | 4 | Using where; Using index | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ mysql> explain select id,name from tbl_user where name like "%aa%"; +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | 1 | SIMPLE | tbl_user | index | NULL | idx_user_nameAge | 68 | NULL | 4 | Using where; Using index | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ mysql> explain select id,name,age from tbl_user where name like "%aa%"; +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | 1 | SIMPLE | tbl_user | index | NULL | idx_user_nameAge | 68 | NULL | 4 | Using where; Using index | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ mysql> explain select name,age from tbl_user where name like "%aa%"; +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ | 1 | SIMPLE | tbl_user | index | NULL | idx_user_nameAge | 68 | NULL | 4 | Using where; Using index | +----+-------------+----------+-------+---------------+------------------+---------+------+------+--------------------------+ mysql> explain select * from tbl_user where name like "%aa%"; +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tbl_user | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+----------+------+---------------+------+---------+------+------+-------------+
mysql> explain select * from staffs where name=2000;(隱形的數字轉換)
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | idx_staffs_nap | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
mysql> explain select * from staffs where name="2000";
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-------------+
| 1 | SIMPLE | staffs | ref | idx_staffs_nap | idx_staffs_nap | 74 | const | 1 | Using where |
+----+-------------+--------+------+----------------+----------------+---------+-------+------+-------------+
==>字符串不加引號索引失效
mysql> explain select * from staffs where name='July' or name='z3';
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
| 1 | SIMPLE | staffs | ALL | idx_staffs_nap | NULL | NULL | NULL | 3 | Using where |
+----+-------------+--------+------+----------------+------+---------+------+------+-------------+
==>少用or,用它來鏈接時會索引失效
測試數據,建立聯合索引,觀察相關執行狀況 create table test03 (
id int primary key not null auto_increment,
c1 char(10),
c2 char(10),
c3 char(10),
c4 char(10),
c5 char(10)
); insert into test03(c1,c2,c3,c4,c5)values('a1','a2','a3','a4','a5'); insert into test03(c1,c2,c3,c4,c5)values('b1','b2','b3','b4','b5'); insert into test03(c1,c2,c3,c4,c5)values('c1','c2','c3','c4','c5'); insert into test03(c1,c2,c3,c4,c5)values('d1','d2','d3','d4','d5'); insert into test03(c1,c2,c3,c4,c5)values('e1','e2','e3','e4','e5'); create index inx_c1234 on test03(c1,c2,c3,c4);
mysql> explain select * from test03 where c1='a1';
+----+-------------+--------+------+---------------+-----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------+------+-------------+
| 1 | SIMPLE | test03 | ref | inx_c1234 | inx_c1234 | 31 | const | 1 | Using where |
+----+-------------+--------+------+---------------+-----------+---------+-------+------+-------------+
mysql> explain select * from test03 where c1='a1' and c2='a2';
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-------------+
| 1 | SIMPLE | test03 | ref | inx_c1234 | inx_c1234 | 62 | const,const | 1 | Using where |
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-------------+
mysql> explain select * from test03 where c1='a1' and c2='a2' and c3='a3';
+----+-------------+--------+------+---------------+-----------+---------+-------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------------------+------+-------------+
| 1 | SIMPLE | test03 | ref | inx_c1234 | inx_c1234 | 93 | const,const,const | 1 | Using where |
+----+-------------+--------+------+---------------+-----------+---------+-------------------+------+-------------+
mysql> explain select * from test03 where c1='a1' and c2='a2' and c3='a3' and c4='a4';
+----+-------------+--------+------+---------------+-----------+---------+-------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------------------------+------+-------------+
| 1 | SIMPLE | test03 | ref | inx_c1234 | inx_c1234 | 124 | const,const,const,const | 1 | Using where |
+----+-------------+--------+------+---------------+-----------+---------+-------------------------+------+-------------+
mysql> explain select * from test03 where c1='a1' and c2='a2' and c4='a4' and c3='a3';
+----+-------------+--------+------+---------------+-----------+---------+-------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------------------------+------+-------------+
| 1 | SIMPLE | test03 | ref | inx_c1234 | inx_c1234 | 124 | const,const,const,const | 1 | Using where |
+----+-------------+--------+------+---------------+-----------+---------+-------------------------+------+-------------+
mysql> explain select * from test03 where c4='a4' and c3='a3' and c2='a2' and c1='a1';
+----+-------------+--------+------+---------------+-----------+---------+-------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------------------------+------+-------------+
| 1 | SIMPLE | test03 | ref | inx_c1234 | inx_c1234 | 124 | const,const,const,const | 1 | Using where |
+----+-------------+--------+------+---------------+-----------+---------+-------------------------+------+-------------+
==>MYSQL OPTIMIZER查詢優化器起的做用
mysql> explain select * from test03 where c1='a1' and c2='a2' and c3>'a3' and c4='a4';
==>範圍後索引失效,c3用在了排序
mysql> explain select * from test03 where c1='a1' and c2='a2' and c3>'a3' and c4='a4';
+----+-------------+--------+-------+---------------+-----------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+-----------+---------+------+------+-------------+
| 1 | SIMPLE | test03 | range | inx_c1234 | inx_c1234 | 93 | NULL | 1 | Using where |
+----+-------------+--------+-------+---------------+-----------+---------+------+------+-------------+
mysql> explain select * from test03 where c1='a1' and c2='a2' and c4>'a4' and c3='a3';
+----+-------------+--------+-------+---------------+-----------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+-----------+---------+------+------+-------------+
| 1 | SIMPLE | test03 | range | inx_c1234 | inx_c1234 | 124 | NULL | 1 | Using where |
+----+-------------+--------+-------+---------------+-----------+---------+------+------+-------------+
==>用到了4個,c4>'a4'範圍以後全失效,可是c4是最後一個,因此。
mysql> explain select * from test03 where c1='a1' and c2='a2' and c4='a4' order by c3;
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-------------+
| 1 | SIMPLE | test03 | ref | inx_c1234 | inx_c1234 | 62 | const,const | 1 | Using where |
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-------------+
==>c3用到了,可是用在了排序,而不是查找
mysql> explain select * from test03 where c1='a1' and c2='a2' order by c3;
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-------------+
| 1 | SIMPLE | test03 | ref | inx_c1234 | inx_c1234 | 62 | const,const | 1 | Using where |
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-------------+
==>效果同上
mysql> explain select * from test03 where c1='a1' and c2='a2' order by c4;
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-----------------------------+
| 1 | SIMPLE | test03 | ref | inx_c1234 | inx_c1234 | 62 | const,const | 1 | Using where; Using filesort |
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-----------------------------+
==>c3斷了,Using filesort
mysql> explain select * from test03 where c1='a1' and c5='a5' order by c2,c3;
+----+-------------+--------+------+---------------+-----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------+------+-------------+
| 1 | SIMPLE | test03 | ref | inx_c1234 | inx_c1234 | 31 | const | 1 | Using where |
+----+-------------+--------+------+---------------+-----------+---------+-------+------+-------------+
==>只用c1字段排序,可是c2,c3用於排序,所以無Using filesort,若是後面order by c3,c4會出現
mysql> explain select * from test03 where c1='a1' and c2='a2' order by c3,c2;
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-------------+
| 1 | SIMPLE | test03 | ref | inx_c1234 | inx_c1234 | 62 | const,const | 1 | Using where |
+----+-------------+--------+------+---------------+-----------+---------+-------------+------+-------------+
==>由於排序字段c2已是個常量,即c2是一個惟一值。
mysql> explain select * from test03 where c1='a1' and c4='a4' group by c2,c3;
+----+-------------+--------+------+---------------+-----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------+------+-------------+
| 1 | SIMPLE | test03 | ref | inx_c1234 | inx_c1234 | 31 | const | 1 | Using where |
+----+-------------+--------+------+---------------+-----------+---------+-------+------+-------------+
mysql> explain select * from test03 where c1='a1' and c4='a4' group by c3,c2;
+----+-------------+--------+------+---------------+-----------+---------+-------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+-----------+---------+-------+------+----------------------------------------------+
| 1 | SIMPLE | test03 | ref | inx_c1234 | inx_c1234 | 31 | const | 1 | Using where; Using temporary; Using filesort |
+----+-------------+--------+------+---------------+-----------+---------+-------+------+-----------------------------------------
==>group by基本上都須要排序,會有臨時表產生(分組以前必排序)
索引優化通常性建議
一、對於單鍵索引,儘可能選擇對當前query過濾性更好的索引
二、在選擇組合索引的時候,當前query中過濾性最好的字段在索引字段順序中,位置越靠前越好
三、在選擇組合索引的時候,儘可能選擇可能可以包含當前query中的where字句中更多字段的索引
四、儘量經過分析統計信息和調整query的寫法來達到選擇合適索引的目的
SQL語句查詢優化原則:小表驅動大表
#################原理######### select * from A where id in (select id from B) 等價於 for select id from B for select * from A where A.id=B.id 當B表的數據集必須小於A表的數據集時,用in優於exist select * from A where exists (select 1 from B where B.id=A.id) 等價於 for select * from A for select * from B where B.id=A.id 當A表的數據集小於B表的數據集時,用exist優於in(A與B表的ID字段應該創建索引) EXISTS select ... from table where exists(subquery) 該語法能夠理解爲,將主查詢的數據,放到子查詢中作條件驗證,根據驗證結果(TRUE或FALSE),來決定主查詢的數據結果是否得以保留 EXISTS(subquery)只返回TRUE或FALSE EXISTS(subquery)實際過程當中可能通過了優化而不是咱們理解上的逐條對比 EXISTS(subquery)每每也可使用條件表達式,其餘子查詢或者join來替代,何種最優須要具體分析
Order by優化
reate table tblA ( age int, birth timestamp not null ); insert into tblA(age,birth)values(22,now()); insert into tblA(age,birth)values(23,now()); insert into tblA(age,birth)values(24,now()); create index idx_ab on tblA(age,birth);
mysql> explain select * from tblA where age >20 order by age;
+----+-------------+-------+-------+---------------+--------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+--------+---------+------+------+--------------------------+
| 1 | SIMPLE | tblA | index | idx_ab | idx_ab | 9 | NULL | 3 | Using where; Using index |
+----+-------------+-------+-------+---------------+--------+---------+------+------+--------------------------+
mysql> explain select * from tblA where age >20 order by birth;
+----+-------------+-------+-------+---------------+--------+---------+------+------+------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+--------+---------+------+------+------------------------------------------+
| 1 | SIMPLE | tblA | index | idx_ab | idx_ab | 9 | NULL | 3 | Using where; Using index; Using filesort |
+----+-------------+-------+-------+---------------+--------+---------+------+------+------------------------------------------+
mysql> explain select * from tblA where age >20 order by age,birth;
+----+-------------+-------+-------+---------------+--------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+--------+---------+------+------+--------------------------+
| 1 | SIMPLE | tblA | index | idx_ab | idx_ab | 9 | NULL | 3 | Using where; Using index |
+----+-------------+-------+-------+---------------+--------+---------+------+------+--------------------------+
mysql> explain select * from tblA order by age asc,birth desc;
+----+-------------+-------+-------+---------------+--------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+--------+---------+------+------+-----------------------------+
| 1 | SIMPLE | tblA | index | NULL | idx_ab | 9 | NULL | 3 | Using index; Using filesort |
+----+-------------+-------+-------+---------------+--------+---------+------+------+-----------------------------+
==>order by默認升序
order by排序結論
Mysql支持FileSort和Index方式排序,Index方式效率高(掃描索引),並儘量的在索引列上完成排序操做,遵循索引創建的最佳左前綴原則
若是不在索引列上,filesort有兩種算法:雙路排序和單路排序 雙路排序
雙路排序:mysql4.1前使用雙路排序,掃描兩次磁盤,獲得數據。先讀取行指針和order by的列,進行排序,而後掃描已經排序好的列表,按照列表中的值從新從列表中讀取對應的數據輸出
單路排序:從磁盤讀取查詢須要的全部列,按照order by列在buffer對他們進行排序,而後掃描排序後的列表進行輸出,效率更快些,避免二次讀取數據,而且把隨機IO變成順序IO,可是須要更多的空間,由於它把每行都保存在內存中了
在sort_buffer中,方法B要比A暫用更多空間,因此有可能取出的數據總大小超出其容量,致使每次只能讀取其相應容量的數據,進行排序(建立tmp文件,多路合併),排完再取,再排,從而屢次IO,反而致使大量IO
提升order by速度
一、order by使用select *是大忌
當query字段大小總和小於max_lenth_for_sort_data並且字段不是text或blob類型時,會使用單路,不然使用多路
兩種算法都有可能超出sort_buffer容量,超出的話使用單路風險更大,因此能夠提升sort_buffer_size
二、嘗試提升sort_buffer_size
對兩種算法都有效率提高,要注意此參數針對每一個進程
三、嘗試提升max_lenth_for_sort_data
會增長使用改進算法的機率,可是入股哦過高,數據總量超過sort_buffer_size的機率就增大,明顯症狀是高的磁盤IO和低的cpu使用效率
order by知足兩種狀況會使用Index方式排序
一、order by語句使用索引最左前列
二、使用where子句與order by子句條件列組合知足索引最左前列
爲排序使用索引
Mysql兩種排序方式:文件排序和掃描有序索引排序
Mysql能爲排序和查詢使用相同的索引
key a_b_c(a,b,c)
order by使用索引前綴生效
order by a order by a,b order by a,b,c
order by a desc,b desc,c desc 要麼同升,要麼同降
若是where使用最左前綴定義爲常量,則order by能使用索引
where a = const order by b,c
where a = const and b =const order by c
where a = const and b>const order by b,c
不能使用索引進行排序
order by a asc,b desc,c desc 排序不一致
where g=const order by b,c 丟失a索引
where a=const order by c 丟失b索引
where a=const order by a,d d不是索引的一部分
where a in (...) order by b,c 對於排序來講,多個相等條件也是範圍查詢
group by關鍵字優化
實質是先排序後分組,遵守索引建的最佳左前綴
當沒法使用索引列,增大max_length_for_sort_data參數的設置+增大sort_buffer_size參數的設置
where高於having,能寫在where限定的條件就不要去having限定了
使用explain優化SQL語句的基本流程(通常讓開發去優化)
一、抓慢查詢,而後使用explain語句檢查索引執行狀況
a、mysql> show full processlist\G (現場抓)或#mysql -uroot -prootabcd -e "show processlist;" | grep -i select(效果更好) ,以後能夠用explain或profile分析
b、分析慢查詢日誌(平時運維)使用mysqlsla工具分析(自帶的mysqldumpslow比較簡陋)
long_query_time=2 超過2秒以上的查詢記錄下來
log-slow-queries=/data/slow.log
log_queries_not_using_indexes 沒走索引的也記錄
mysql> show variables like "%slow_query%";
+---------------------+-----------------------+
| Variable_name | Value |
+---------------------+-----------------------+
| slow_query_log | OFF |
| slow_query_log_file | /data/node80-slow.log |
+---------------------+-----------------------+
mysql> show variables like "%long_query%";
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
mysql> show global status like "%Slow_que%";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Slow_queries | 0 |
+---------------+-------+
mysqldump分析工具
返回記錄集最多的10個sql mysqldumpslow -s r -t 10 slowfile | more
返回訪問次數最多的10個sql mysqldumpslow -s c -t 10 slowfile | more
按時間排序的前10條裏面含有作連接的sql mysqldumpslow -s t -t 10 -g "left join" slowfile | more
對slow.log,進行切割腳本分析 切割慢查詢(寫成腳本,使用mysqlsla分析),自帶mysqldumpslow工具
cd /data/ &&
/bin/mv slow.log /back/$(date +%F)_slow.log
mysqladmin -uroot -prootabcd flush-logs (也會刷新binlog)
mysqlsla /back/$(date +%F)_slow.log > new_/back/$(date +%F)_slow.log
mail -s "logname" mailadress </bakup/ new_/back/$(date +%F)_slow.log
二、explain語句檢查索引執行狀況
三、對須要建索引的列創建索引或者參數調優(最後才考慮調優)
使用explain優化sql語句場景案例
優化原由
一、網站慢,瀏覽慢,假定查出是數據庫問題
二、數據庫服務器查看uptime,負載很高(load average:8.01 6.30,5.58 超過核數數屬於高的)
三、登錄數據庫show full processlist===>再用explain 檢查語句是否走索引,查看建表語句或show index等,
四、慢查詢語句或日誌
五、select count(distinct列) from table 查看字段惟一數 根據這個和開發溝通建立相關索引,若是都很少考慮聯合索引,若是大表,建立須要幾分鐘,建議在業務低谷執行
mysql> show global variables like "profiling";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| profiling | OFF |
+---------------+-------+
注意以下問題
converting HEAP to MyISAM查詢結果太大,內存都不夠用了往磁盤上搬了
Creating tmp table 建立臨時表
Copying to tmp table on disk 把內存中的臨時表複製到磁盤,危險
locked
鎖
鎖是計算機協調多個進程或線程併發訪問某一資源的機制
根據操做類型分
讀鎖(共享鎖):針對同一份數據,多個讀操做能夠同時進行而不會互相影響
寫鎖(排它鎖):當前寫操做沒有完成前,它會阻斷其餘寫鎖和讀鎖
根據數據操做的力度
行鎖
表鎖
myisam表鎖分析
create table mylock( id int not null primary key auto_increment, name varchar(20) ) engine myisam;
insert into mylock(name) values('a');
insert into mylock(name) values('b');
insert into mylock(name) values('c');
insert into mylock(name) values('d');
insert into mylock(name) values('e');
mysql> show open tables; 查看哪些表被加鎖了 +--------------------+----------------------------------------------+--------+-------------+ | Database | Table | In_use | Name_locked | +--------------------+----------------------------------------------+--------+-------------+ | mysql | time_zone_transition_type | 0 | 0 | | performance_schema | events_waits_summary_global_by_event_name | 0 | 0 | | performance_schema | setup_timers | 0 | 0 | | performance_schema | events_waits_history_long | 0 | 0 | | gtms | tblA | 0 | 0 | | performance_schema | mutex_instances | 0 | 0 | | performance_schema | events_waits_summary_by_instance | 0 | 0 | | mysql | tables_priv | 0 | 0 | | gtms | mylock | 0 | 0 |
mysql> show status like "table%";
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Table_locks_immediate | 54 |產生表鎖的次數,表示能夠當即獲取鎖的查詢次數,每當即獲取鎖值加1
| Table_locks_waited | 0 |出現表鎖爭用而發生的等待次數,(不能當即獲取的次數,每等待一次加1),此值高說明存在較嚴重的表鎖爭用
+-----------------------+-------+
加解鎖的方法
mysql> lock table mylock read;
mysql> lock table mylock write;
mysql> unlock tables;
進行測試
mysql> lock table mylock read; 增長讀鎖
mysql> update mylock set name='a2' where id=1;
ERROR 1099 (HY000): Table 'mylock' was locked with a READ lock and can't be updated 不能進行更新
mysql> select * from article;
ERROR 1100 (HY000): Table 'article' was not locked with LOCK TABLES 也不能讀其餘表
==>只能讀本身,不能改本身,也不能讀其餘表
==>再開一個sesson,嘗試update,會阻塞,等待解鎖後被執行。可想而知,效率呢?
mysql> unlock tables;
mysql> lock table mylock write;
mysql> update mylock set name='a2' where id=1;
mysql> select * from article; 不能讀其餘表
ERROR 1100 (HY000): Table 'article' was not locked with LOCK TABLES
==>再開個session,對mylock進行讀操做,阻塞(備註:測試時換成不一樣ID,由於第二次條件會從緩存得到,影響鎖效果演示)
mysql表鎖結論
一、讀鎖,不會阻塞其餘進程對同一表的讀請求,可是會阻塞對同一表的寫請求,只有釋放後,纔會執行其餘進程的操做
二、寫鎖,會阻塞其餘進程對同一表的讀寫操做,釋放後,纔會執行其餘進程的讀寫操做
==>簡而言之,讀會阻塞寫,可是不會阻塞讀,寫鎖則會把讀和寫都阻塞
此外,myisam的讀寫鎖調度室寫優先,這也是不適合做爲主表的引擎,由於寫鎖後,其餘線程不能作任何操做,大量的更新使查詢很可貴鎖,從而形成永遠阻塞
InnoDB與MyISAM最大不一樣點:支持事務及行級鎖(行鎖會變成表鎖,由於char類型沒加"")
1、原子性(Atomicity):事務中的所有操做在數據庫中是不可分割的,要麼所有完成,要麼均不執行。 2、一致性(Consistency):幾個並行執行的事務,其執行結果必須與按某一順序串行執行的結果相一致。 3、隔離性(Isolation):事務的執行不受其餘事務的干擾,事務執行的中間結果對其餘事務必須是透明的。(未提交讀,已提交讀,可重複讀,可序列化4個級別) 好比--single-transaction 4、持久性(Durability):對於任意已提交事務,系統必須保證該事務對數據庫的改變不被丟失,即便數據庫出現故障。一個事務一旦被提交,它對數據庫中的數據改變就是永久性的,若是出了錯誤,事務也不容許撤銷,只能經過「補償性事務」
間隙鎖
當咱們用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖; 對於鍵值在條件範圍內但並不存在的記錄,叫作「間隙(GAP)」,InnoDB也會對這個「間隙」加鎖,這種鎖機制就是所謂的間隙鎖(Next-Key鎖)。 例如 update table set b="444" where a>1 and a<6 (其中記錄4不存在) 當其餘session須要插入記錄4時,會被阻塞,直到以前的update語句commit後才能執行 InnoDB使用間隙鎖的目的,一方面是爲了防止幻讀,以知足相關隔離級別的要求, 對於上面的例子,要是不使用間隙鎖,若是其餘事務插入了a>1 and a<6 任何記錄,那麼本事務若是再次執行上述語句,就會發生幻讀; 很顯然,在使用範圍條件檢索並鎖定記錄時,InnoDB這種加鎖機制會阻塞符合條件範圍內鍵值的併發插入,這每每會形成嚴重的鎖等待 所以,在實際應用開發中,尤爲是併發插入比較多的應用,咱們要儘可能優化業務邏輯,儘可能使用相等條件來訪問更新數據,避免使用範圍條件。 InnoDB除了經過範圍條件加鎖時使用間隙鎖外,若是使用相等條件請求給一個不存在的記錄加鎖,InnoDB也會使用間隙鎖
如何鎖定一行
mysql>begin mysql>select * from tableA where a=8 for update;
xxxxxxxxxxxxxxxxxxxx msyql>commit;
分析行鎖爭奪狀況
mysql> show status like "innodb_row_lock%"; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | Innodb_row_lock_current_waits | 0 |當前正在等待鎖定的數量 | Innodb_row_lock_time | 0 |從啓動到如今鎖定總時間長度 | Innodb_row_lock_time_avg | 0 |每次等待所花平均時間 | Innodb_row_lock_time_max | 0 |從系統啓動到如今等待最長一次所花時間 | Innodb_row_lock_waits | 0 |系統啓動後到如今總共等待的次數 +-------------------------------+-------+行鎖分析 儘量讓全部數據檢索都經過索引完成,避免無索引行鎖升級爲表鎖 合理設計索引,儘可能縮小鎖的範圍 儘量減小檢索條件,避免間隙鎖 儘可能控制事務大小,減小鎖定資源量和時間長度 儘量低級別事務隔離