去掉不應鏈接的表

數據庫收到報警,負載飆至60,上去一看,cpu飆至3000%,32個核心所剩無幾!mysql

第一反應,sql語句卡住了。sql

登陸mysql,show processlist,發現大量重複的sql語句數據庫

SELECT cv.filter_value_id,cv.filter_id,cv.value,COUNT(1) quantity
FROM e_category_filter_value AS cv
INNER JOIN e_product_to_filter_value AS p_v ON cv.filter_value_id=p_v.filter_value_id
INNER JOIN products AS p ON p_v.products_id = p.products_id
INNER JOIN products_to_categories AS p_c ON p_c.products_id=p_v.products_id
WHERE cv.filter_id IN(575,576,568,572,569,570,571,573,574)
GROUP BY cv.filter_value_id;

已經有人反應網站開始打不開,爲了儘快解決問題,使用本身寫的快速殺鏈接腳本殺掉這些sql語句.腳本大體能夠參考sql語句ide

SELECT * FROM information_schema.processlist WHERE TIME >=5 AND USER LIKE 'banggood%' AND (state LIKE 'Copying%' OR state LIKE 'Sending%' OR state LIKE 'Sorting%'

連續殺了幾回,發現cpu穩定,網站也趨於穩定!優化

接下來着手優化這條sql語句!網站

第一步,找到對應的開發人員,瞭解大體做用。設計

第二步,explianorm

mysql> explain SELECT SQL_NO_CACHE cv.filter_value_id,cv.filter_id,cv.value,COUNT(1) quantity FROM e_category_filter_value AS cv  INNER JOIN e_product_to_filter_value AS p_v ON cv.filter_value_id=p_v.filter_value_id INNER JOIN products AS p ON p_v.products_id = p.products_id INNER JOIN products_to_categories AS p_c ON p_c.products_id=p_v.products_id WHERE cv.filter_id IN(575,576,568,572,569,570,571,573,574) GROUP BY cv.filter_value_id;
+----+-------------+-------+--------+---------------------------------+-------------+---------+------------------------------+-------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                   | key         | key_len | ref                          | rows  | Extra                                        |
+----+-------------+-------+--------+---------------------------------+-------------+---------+------------------------------+-------+----------------------------------------------+
|  1 | SIMPLE      | p_c   | index  | PRIMARY                         | PRIMARY     | 8       | NULL                         | 98109 | Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | p     | eq_ref | PRIMARY                         | PRIMARY     | 4       | banggood.p_c.products_id     |     1 | Using index                                  |
|  1 | SIMPLE      | p_v   | ref    | products_id,ix_eptfv_fvid_proid | products_id | 4       | banggood.p_c.products_id     |    16 |                                              |
|  1 | SIMPLE      | cv    | eq_ref | PRIMARY,filter_id               | PRIMARY     | 4       | banggood.p_v.filter_value_id |     1 | Using where                                  |
+----+-------------+-------+--------+---------------------------------+-------------+---------+------------------------------+-------+----------------------------------------------+

發現並無使用索引cv.filter_id(該索引是存在的),而是選擇了p_c表的主鍵,致使了全索引掃描,大量損耗cpu。索引

第三步,嘗試使用force indexssl

mysql> EXPLAIN
    -> SELECT SQL_NO_CACHE cv.filter_value_id,cv.filter_id,cv.value,COUNT(1) quantity
    -> FROM e_category_filter_value AS cv  FORCE INDEX(filter_id)
    -> INNER JOIN e_product_to_filter_value AS p_v ON cv.filter_value_id=p_v.filter_value_id
    -> INNER JOIN products AS p ON p_v.products_id = p.products_id
    -> INNER JOIN products_to_categories AS p_c ON p_c.products_id=p_v.products_id
    -> WHERE cv.filter_id IN(575,576,568,572,569,570,571,573,574)
    -> GROUP BY cv.filter_value_id;
+----+-------------+-------+--------+---------------------------------+---------------------+---------+-----------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                   | key                 | key_len | ref                         | rows | Extra                                        |
+----+-------------+-------+--------+---------------------------------+---------------------+---------+-----------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | cv    | range  | filter_id                       | filter_id           | 4       | NULL                        |   75 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | p_v   | ref    | products_id,ix_eptfv_fvid_proid | ix_eptfv_fvid_proid | 4       | banggood.cv.filter_value_id | 1495 | Using index                                  |
|  1 | SIMPLE      | p_c   | ref    | PRIMARY                         | PRIMARY             | 4       | banggood.p_v.products_id    |  981 | Using index                                  |
|  1 | SIMPLE      | p     | eq_ref | PRIMARY                         | PRIMARY             | 4       | banggood.p_c.products_id    |    1 | Using where; Using index                     |
+----+-------------+-------+--------+---------------------------------+---------------------+---------+-----------------------------+------+----------------------------------------------+

對比兩次執行時間,爲使用force index時,執行須要2s;使用後,執行時間變爲0.03s。

你覺得此次優化就這樣結束了,那就和我給的題目不符了!

第四部:

仔細觀察這個sql語句,發現where裏面和select子句裏面都麼有設計到

INNER JOIN products AS p ON p_v.products_id = p.products_id
INNER JOIN products_to_categories AS p_c ON p_c.products_id=p_v.products_id

這兩個連表子句的任何參數,詢問開發,發現須要和products表作連表過濾,由於可能在e_category_filter_value表中存在的products_id,可是可能在products表中不存在。而至於products_to_categories表(產品對類別表,一個產品對應了多個類別,是一個能夠將結果集放大很是多倍的表),他找不到加上的理由。

如今去掉products_to_categories表

mysql> EXPLAIN
    -> SELECT SQL_NO_CACHE cv.filter_value_id,cv.filter_id,cv.value,COUNT(1) quantity
    -> FROM e_category_filter_value AS cv
    -> INNER JOIN e_product_to_filter_value AS p_v ON cv.filter_value_id=p_v.filter_value_id
    -> INNER JOIN products AS p ON p_v.products_id = p.products_id
    -> WHERE cv.filter_id IN(575,576,568,572,569,570,571,573,574)
    -> GROUP BY cv.filter_value_id;
+----+-------------+-------+--------+---------------------------------+---------------------+---------+-----------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                   | key                 | key_len | ref                         | rows | Extra                                        |
+----+-------------+-------+--------+---------------------------------+---------------------+---------+-----------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | cv    | range  | PRIMARY,filter_id               | filter_id           | 4       | NULL                        |   75 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | p_v   | ref    | products_id,ix_eptfv_fvid_proid | ix_eptfv_fvid_proid | 4       | banggood.cv.filter_value_id | 1495 | Using index                                  |
|  1 | SIMPLE      | p     | eq_ref | PRIMARY                         | PRIMARY             | 4       | banggood.p_v.products_id    |    1 | Using index                                  |
+----+-------------+-------+--------+---------------------------------+---------------------+---------+-----------------------------+------+----------------------------------------------+
3 rows in set (0.00 sec)

發現索引使用正確,再次執行,發現執行時間變爲0.03s。


總結:1.儘可能不要鏈接一些無關緊要的表,這個例子就是血的教訓

2.不要太相信mysql的索引使用,有的時候須要本身藉助於force index命令來進行調優!