數據庫收到報警,負載飆至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命令來進行調優!