1. 程式人生 > >Mysql中的force index和ignore index

Mysql中的force index和ignore index

前幾天統計一個sql,是一個人提交了多少工單,順便做了相關sql優化。資料大概2000多w。

select  CustName,count(1) c from WorkOrder  where CreateDate>'2016-5-1' and CreateDate<'2017-1-1'
group by CustName having c>100 
order by c desc;

  為了實驗最少受其他因素干擾,將生產庫的200多w資料匯出來,用測試伺服器進行測試。

  匯出來的資料是一個堆表,沒有主鍵,沒有索引。

mysql> show index from WorkOrder;   查詢index方法1
Empty set (0.00 sec)

mysql> show keys from WorkOrder;    查詢index方法2
Empty set (0.00 sec)

 1.堆表的情況

  這時候就在這時候,用執行計劃分析下語句。

複製程式碼

mysql>  explain select  CustName,count(1) c from WorkOrder 
 where CreateDate>'2016-5-1' and CreateDate<'2017-1-1' group by CustName having c>100 order by c desc;
+----+-------------+-----------+------+---------------+------+---------+------+---------+----------------------------------------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows    | Extra                                        |
+----+-------------+-----------+------+---------------+------+---------+------+---------+----------------------------------------------+
|  1 | SIMPLE      | WorkOrder | ALL  | NULL          | NULL | NULL    | NULL | 2528727 | Using where; Using temporary; Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+---------+----------------------------------------------+
1 row in set

複製程式碼

  select_type的值為SIMPLE,表示簡單的select查詢,不使用union或子查詢。

  type的值為ALL,表示要對錶進行表掃描。

  possible_keys 表示能使用哪個索引找到行記錄。

  key 表示Mysql決定使用的索引(鍵)。

       key_len 表示Mysql決定使用索引的長度。

  ref  表示使用哪個列和key一起從表中選擇行。

  rows 表示Mysql認為它執行查詢時必須檢查的行數。

  extra 表示查詢的詳情資訊,用到where,臨時表,排序。

  執行下該語句三次,發現執行了16.30 sec、16.34 sec、16.24 sec。

  2.有索引的情況

  建了四個索引,分別以custname,CreateDate建兩個單列索引,另外兩個是聯合索引,只是最左邊列不一樣。

alter table WorkOrder add index ix_name(custname)  
alter table WorkOrder add index ix_date(CreateDate)  
alter table WorkOrder add index ix_namedate(custname,CreateDate)  
alter table WorkOrder add index ix_datename(CreateDate,custname)  

複製程式碼

mysql> show keys from WorkOrder;
+-----------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table     | Non_unique | Key_name    | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| WorkOrder |          1 | ix_name     |            1 | CustName    | A         |     1264363 |     NULL | NULL   | YES  | BTREE      |         |               |
| WorkOrder |          1 | ix_date     |            1 | CreateDate  | A         |     2528727 |     NULL | NULL   |      | BTREE      |         |               |
| WorkOrder |          1 | ix_namedate |            1 | CustName    | A         |     1264363 |     NULL | NULL   | YES  | BTREE      |         |               |
| WorkOrder |          1 | ix_namedate |            2 | CreateDate  | A         |     2528727 |     NULL | NULL   |      | BTREE      |         |               |
| WorkOrder |          1 | ix_datename |            1 | CreateDate  | A         |     2528727 |     NULL | NULL   |      | BTREE      |         |               |
| WorkOrder |          1 | ix_datename |            2 | CustName    | A         |     2528727 |     NULL | NULL   | YES  | BTREE      |         |               |
+-----------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
6 rows in set (0.00 sec)

複製程式碼

  之後,用執行計劃分析下sql查詢語句。

複製程式碼

mysql> explain select  CustName,count(1) c from WorkOrder 
     where CreateDate>'2016-5-1' and CreateDate<'2017-1-1' group by CustName having c>100 order by c desc;
+----+-------------+-----------+-------+-----------------------------------------+-------------+---------+------+--------+-----------------------------------------------------------+
| id | select_type | table     | type  | possible_keys                           | key         | key_len | ref  | rows   | Extra                                                     |
+----+-------------+-----------+-------+-----------------------------------------+-------------+---------+------+--------+-----------------------------------------------------------+
|  1 | SIMPLE      | WorkOrder | range | ix_name,ix_date,ix_namedate,ix_datename | ix_datename | 4       | NULL | 824372 | Using where; Using index; Using temporary; Using filesort |
+----+-------------+-----------+-------+-----------------------------------------+-------------+---------+------+--------+-----------------------------------------------------------+
1 row in set (0.01 sec)

複製程式碼

  從執行計劃可以看出,Mysql從四個索引中選取了ix_datename這個索引,type為range表示索引範圍掃描。rows的數量值是沒堆表的1/3。

  執行語句三次,時間是 8.64 sec、8.61sec、8.55 sec。

我建了三個索引,那麼我想用下另外三個索引怎麼辦?

   這裡可以用force index(),這個指令可以指定本次查詢強制使用哪個索引,因為Mysql優化器的選擇並不是最優的索引。

複製程式碼

mysql> explain select  CustName,count(1) c from WorkOrder force index(ix_namedate) 
where CreateDate>'2016-5-1' and CreateDate<'2017-1-1' group by CustName having c>100 order by c desc;
+----+-------------+-----------+-------+---------------------------------+-------------+---------+------+---------+-----------------------------------------------------------+
| id | select_type | table     | type  | possible_keys                   | key         | key_len | ref  | rows    | Extra                                                     |
+----+-------------+-----------+-------+---------------------------------+-------------+---------+------+---------+-----------------------------------------------------------+
|  1 | SIMPLE      | WorkOrder | index | ix_name,ix_namedate,ix_datename | ix_namedate | 307     | NULL | 2528727 | Using where; Using index; Using temporary; Using filesort |
+----+-------------+-----------+-------+---------------------------------+-------------+---------+------+---------+-----------------------------------------------------------+

複製程式碼

    選用另一個聯合索引 ix_namedate,這次type變為index,可以這樣理解,根據索引的順序進行全表掃描,比ALL效率要高些,rows的值和堆表的值差不多。

    執行語句三次,時間是 7.84 sec、7.92 sec、7.84 sec。

複製程式碼

mysql> explain select  CustName,count(1) c from WorkOrder force index(ix_name) 
where CreateDate>'2016-5-1' and CreateDate<'2017-1-1' group by CustName having c>100 order by c desc;
+----+-------------+-----------+-------+---------------------------------+---------+---------+------+---------+----------------------------------------------+
| id | select_type | table     | type  | possible_keys                   | key     | key_len | ref  | rows    | Extra                                        |
+----+-------------+-----------+-------+---------------------------------+---------+---------+------+---------+----------------------------------------------+
|  1 | SIMPLE      | WorkOrder | index | ix_name,ix_namedate,ix_datename | ix_name | 303     | NULL | 2528727 | Using where; Using temporary; Using filesort |
+----+-------------+-----------+-------+---------------------------------+---------+---------+------+---------+----------------------------------------------+
1 row in set

複製程式碼

    選用另一個聯合索引 ix_name,這次type是index,可以這樣理解,根據索引的順序進行全表掃描,比ALL效率要高些,rows的值和堆表的值差不多。

    執行語句三次,時間是 1 min 28.17 sec、1 min 27.64 sec、1 min 27.58 sec。

複製程式碼

mysql> explain select  CustName,count(1) c from WorkOrder force index(ix_date) 
where CreateDate>'2016-5-1' and CreateDate<'2017-1-1' group by CustName having c>100 order by c desc;
+----+-------------+-----------+-------+-----------------------------------------+---------+---------+------+--------+-------------------------------------------------------------------+
| id | select_type | table     | type  | possible_keys                           | key     | key_len | ref  | rows   | Extra                                                             |
+----+-------------+-----------+-------+-----------------------------------------+---------+---------+------+--------+-------------------------------------------------------------------+
|  1 | SIMPLE      | WorkOrder | range | ix_name,ix_date,ix_namedate,ix_datename | ix_date | 4       | NULL | 921062 | Using index condition; Using MRR; Using temporary; Using filesort |
+----+-------------+-----------+-------+-----------------------------------------+---------+---------+------+--------+-------------------------------------------------------------------+

複製程式碼

    選用另一個聯合索引 ix_date,這次type是range,表示索引範圍掃描,rows的值是堆表的1/3多些 。

    執行語句三次,時間是 9.55 sec、9.52 sec、9.39 sec。

假如我不想用索引了怎麼辦?

   可以使用ignore index(),這個指令可以強制Mysql在查詢時,不使用某索引。

複製程式碼

mysql> explain select  CustName,count(1) c from WorkOrder  ignore index(ix_date) 
where CreateDate>'2016-5-1' and CreateDate<'2017-1-1' group by CustName having c>100 order by c desc;
+----+-------------+-----------+-------+---------------------------------+-------------+---------+------+--------+-----------------------------------------------------------+
| id | select_type | table     | type  | possible_keys                   | key         | key_len | ref  | rows   | Extra                                                     |
+----+-------------+-----------+-------+---------------------------------+-------------+---------+------+--------+-----------------------------------------------------------+
|  1 | SIMPLE      | WorkOrder | range | ix_name,ix_namedate,ix_datename | ix_datename | 4       | NULL | 824372 | Using where; Using index; Using temporary; Using filesort |
+----+-------------+-----------+-------+---------------------------------+-------------+---------+------+--------+-----------------------------------------------------------+

mysql> explain select  CustName,count(1) c from WorkOrder  ignore index(ix_date,ix_name,ix_namedate,ix_datename) 
where CreateDate>'2016-5-1' and CreateDate<'2017-1-1' group by CustName having c>100 order by c desc;
+----+-------------+-----------+------+---------------------------------+------+---------+------+---------+----------------------------------------------+
| id | select_type | table     | type | possible_keys                   | key  | key_len | ref  | rows    | Extra                                        |
+----+-------------+-----------+------+---------------------------------+------+---------+------+---------+----------------------------------------------+
|  1 | SIMPLE      | WorkOrder | ALL  | ix_name,ix_namedate,ix_datename | NULL | NULL    | NULL | 2528727 | Using where; Using temporary; Using filesort |
+----+-------------+-----------+------+---------------------------------+------+---------+------+---------+----------------------------------------------+

複製程式碼

  上面第一個強制不使用ix_date索引,那麼就Mysql就從剩下的三個索引中,選取他認為是最優的索引。第二個時將四個索引都不使用,那麼Mysql就進行全表掃描了。

  總結:

      1.Mysql的語句優化,沒有絕對的正確,explain也只是給出個大致的方向,例如 key_len值小的,rows小的按理說,時間應該最短,效率最高。但是,實驗中時間最少的卻不是那個值最小的。

       2. 優化還需根據實際資料情況,例如,假如我where選取的時間範圍變化,或者說CustName的分佈有些變化,可能跟剛才的實驗,又會產生一定偏差。

       3. 同樣我還實驗了,當給表加上主鍵時,整體的查詢時間會縮短些。

------------------附相關index命令--------------

刪除主鍵

ALTER TABLE WorkOrder  MODIFY id int(11); --1.先刪除auto_increment
ALTER TABLE  WorkOrder  DROP PRIMARY KEY;  --2.再刪除主鍵

ALTER TABLE WorkOrder DROP index ix_datename;--刪除索引