通過explain對mysql索引優化

原文地址: http://www.cnblogs.com/zaric/archive/2012/09/28/2707248.html

-----------

今天優化了多條SQL語句。都是EXPLAIN的功勞,分析SQL子句的執行順序和執行情況,一木瞭然,下來看具體分析:

[優化多表聯合查詢]

複製代碼
explain SELECT sql_no_cache pker.*,pk.*  FROM ng_game_pk AS pk ,ng_game_pker AS pker  where pker.pkid = pk.id and (pker.act_uid = 1 OR pker.def_uid = 1) AND pk.type <>4  GROUP BY pk.id limit 10;
+----+-------------+-------+--------+----------------------------------+---------+---------+--------------------+--------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                    | key     | key_len | ref                | rows   | Extra                                        |
+----+-------------+-------+--------+----------------------------------+---------+---------+--------------------+--------+----------------------------------------------+
|  1 | SIMPLE      | pker  | ALL    | pkid,act_def                     | NULL    | NULL    | NULL               | 177543 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | pk    | eq_ref | PRIMARY,type,idx_type_status_uid | PRIMARY | 4       | pwgbk8.7.pker.pkid |      1 | Using where                                  |
+----+-------------+-------+--------+----------------------------------+---------+---------+--------------------+--------+----------------------------------------------+
複製代碼

key:NULL,,,Extra:Using temporary,Using filesort都會造成語句很慢。type:ALL 全表掃描,沒有比這個更糟糕的了。

下面作出改動:

複製代碼
explain SELECT sql_no_cache pker.*,pk.*  FROM ng_game_pk AS pk ,ng_game_pker AS pker  where pker.pkid = pk.id and (pker.act_uid = 1 OR pker.def_uid = 1) AND pk.type <>4  GROUP BY pker.pkid limit 10;
+----+-------------+-------+--------+----------------------------------+---------+---------+--------------------+------+-------------+
| id | select_type | table | type   | possible_keys                    | key     | key_len | ref                | rows | Extra       |
+----+-------------+-------+--------+----------------------------------+---------+---------+--------------------+------+-------------+
|  1 | SIMPLE      | pker  | index  | pkid,act_def                     | pkid    | 4       | NULL               |   10 | Using where |
|  1 | SIMPLE      | pk    | eq_ref | PRIMARY,type,idx_type_status_uid | PRIMARY | 4       | pwgbk8.7.pker.pkid |    1 | Using where |
+----+-------------+-------+--------+----------------------------------+---------+---------+--------------------+------+-------------+
複製代碼

比較下執行:

複製代碼
mysql> show profile for query 147;
+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| Creating tmp table   | 0.000372 |
| Copying to tmp table | 0.424248 |
| removing tmp table   | 0.002125 |
+----------------------+----------+
複製代碼

這裏忽略了其他的執行。看到拷貝到臨時表佔用了大部分時間。這裏需要解釋下:Copying to tmp table是拷貝到內存,如果是Copying to tmp table on disk意味着內存空間不足,MySQL將會寫臨時表到磁盤。這個大小的配置參考tmp_table_size。

現分析下第二條SQL,,,子句id是一樣的,那麼執行順序是從上至下,現查詢pker表,索引爲pkid,那麼可以認爲GROUP BY pker.pkid作用於上面的子句,rows爲10,那麼limit 10也是作用於此。

[優化索引、複合索引]

複製代碼
mysql> select count(*) from ng_game_pk WHERE status = 0 AND type = 4 AND uid = 1;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (13.03 sec)
mysql> explain select count(*) from ng_game_pk WHERE status = 0 AND type = 4 AND uid = 1;
+----+-------------+------------+------+---------------+------+---------+-------+---------+-------------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref   | rows    | Extra       |
+----+-------------+------------+------+---------------+------+---------+-------+---------+-------------+
|  1 | SIMPLE      | ng_game_pk | ref  | type          | type | 1       | const | 1729551 | Using where |
+----+-------------+------------+------+---------------+------+---------+-------+---------+-------------
複製代碼

這條語句用到了索引type,但是type取值範圍很窄(1,2,3,4)其實這個索引沒多大用處。
下面我們建立複合索引看效果如何,

複製代碼
mysql> alter table ng_game_pk add index idx_type_status_uid(type,status,uid);
Query OK, 5831851 rows affected (1 min 43.20 sec)
Records: 5831851  Duplicates: 0  Warnings: 0
mysql> explain select count(*) from ng_game_pk WHERE status = 0 AND type = 4 AND uid = 1;
+----+-------------+------------+------+--------------------------+---------------------+---------+-------------------+------+-------------+
| id | select_type | table      | type | possible_keys            | key                 | key_len | ref               | rows | Extra       |
+----+-------------+------------+------+--------------------------+---------------------+---------+-------------------+------+-------------+
|  1 | SIMPLE      | ng_game_pk | ref  | type,idx_type_status_uid | idx_type_status_uid | 6       | const,const,const |    1 | Using index |
+----+-------------+------------+------+--------------------------+---------------------+---------+-------------------+------+-------------+
1 row in set (0.11 sec)
mysql> select count(*) from ng_game_pk WHERE status = 0 AND type = 4 AND uid = 1;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)
複製代碼

看來有很好的效果了。對SQL稍作修改,

複製代碼
mysql> select sql_no_cache count(*) from ng_game_pk WHERE type = 4 and status>0 AND uid = 1;
+----------+
| count(*) |
+----------+
|     2649 |
+----------+
1 row in set (0.40 sec)
複製代碼

性能又下來了,這是因爲B-Tree算法的原因,存儲引擎將不能優化任何在第一個條件範圍右邊的列。那麼就是(type,status,,,)後面的索引失效。

那麼調整下,,,

mysql> drop index idx_type_status_uid on ng_game_pk;
mysql> alter table ng_game_pk add index idx_type_uid_status (type,uid,status);

結果性能又提升上來了。

---------------------------EOF--------------------------------

參考:

#《高性能MySql (第二版)》

#《構建高性能web站點》

# http://www.cnitblog.com/aliyiyi08/archive/2008/09/09/48878.html

# http://www.perfgeeks.com/?p=460


發佈了330 篇原創文章 · 獲贊 115 · 訪問量 247萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章