Mysql之索引及優化

   

性能分析
  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語句執行計劃)

  • 表的讀取順序    id
  • 數據讀取操做的操做類型 
  • 哪些索引可使用    possible_keys
  • 哪些索引被實際使用    key
  • 表之間的引用    ref
  • 每張表有多少行被優化器查詢    rows
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 |
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+
View Code

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 |系統啓動後到如今總共等待的次數 +-------------------------------+-------+行鎖分析  儘量讓全部數據檢索都經過索引完成,避免無索引行鎖升級爲表鎖  合理設計索引,儘可能縮小鎖的範圍  儘量減小檢索條件,避免間隙鎖  儘可能控制事務大小,減小鎖定資源量和時間長度  儘量低級別事務隔離
相關文章
相關標籤/搜索