最近在調試一條查詢耗時5s多的sql語句,這條sql語句用到了多表關聯(inner join),按時間字段排序(order by),時間字段上已經建立了索引(索引名IDX_published_at)。經過explain分析發現,時間字段上的索引沒用上(Using temporary和Using filesort),問題很明顯,可是緣由是什麼呢?mysql
SELECT * FROM news n0_ inner join news_translations n1_ ON n0_.id = n1_.translatable_id inner join channels_news c3_ ON n0_.id = c3_.news_id WHERE ((n0_.unpublished_at IS NOT NULL AND (CURRENT_TIMESTAMP >= n0_.published_at AND CURRENT_TIMESTAMP < n0_.unpublished_at)) OR (CURRENT_TIMESTAMP >= n0_.published_at AND n0_.unpublished_at IS NULL)) AND (n0_.status = 1 AND n0_.content_type_id = 1) AND n0_.id NOT IN (510466, 510433, 24, 11, 10, 9, 4) AND n0_.home_position_id IS NULL AND n1_.locale = 'zh_CN' AND c3_.channel_id = 1 ORDER BY n0_.published_at DESC LIMIT 5 ;
優化前sql語句sql
+-------+--------+-------------------------------+--------+-----------------------------------------------------------+
| table | type | key | rows | Extra |
+-------+--------+-------------------------------+--------+-----------------------------------------------------------+
| c3_ | ref | IDX_87B9249E72F5A1AA | 161590 | Using where; Using index; Using temporary; Using filesort |
| n0_ | eq_ref | PRIMARY | 1 | Using where |
| n1_ | ref | UNIQ_20FDB3302C2AC5D34180C698 | 1 | Using where |
+-------+--------+-------------------------------+--------+-----------------------------------------------------------+
explain分析結果 有所刪減性能
通過一輪折騰的優化,獲得了下面的sql語句優化
SELECT * FROM news n0_ STRAIGHT_JOIN news_translations n1_ ON n0_.id = n1_.translatable_id STRAIGHT_JOIN channels_news c3_ ON n0_.id = c3_.news_id WHERE ((n0_.unpublished_at IS NOT NULL AND (CURRENT_TIMESTAMP >= n0_.published_at AND CURRENT_TIMESTAMP < n0_.unpublished_at)) OR (CURRENT_TIMESTAMP >= n0_.published_at AND n0_.unpublished_at IS NULL)) AND (n0_.status = 1 AND n0_.content_type_id = 1) AND n0_.id NOT IN (510466, 510433, 24, 11, 10, 9, 4) AND n0_.home_position_id IS NULL AND n1_.locale = 'zh_CN' AND c3_.channel_id = 1 ORDER BY n0_.published_at DESC LIMIT 5 ;
優化後sql語句spa
+-------+--------+-------------------------------+--------+--------------------------+ | table | type | key | rows | Extra | +-------+--------+-------------------------------+--------+--------------------------+ | n0_ | range | IDX_published_at | 255440 | Using where | | n1_ | ref | UNIQ_20FDB3302C2AC5D34180C698 | 1 | Using where | | c3_ | eq_ref | PRIMARY | 1 | Using where; Using index | +-------+--------+-------------------------------+--------+--------------------------+
優化後explain分析結果 有所刪減調試
優化先後的變化有四點:一、再也不Using temporary和Using filesort;二、表的查詢順尋變了;三、查詢掃描的rows增長了;四、查詢時間由5s降到了0.02s。code
優化先後出現的四點變化,性能顯著提高,須要從mysql的關聯的鏈接處理提及。blog
如下參考《高性能MySQL》排序
1)優化前的sql語句以channels_news爲第一個關聯表,找到161590條記錄;2)優化後的sql語句以news表爲第一關聯表,找到255440條記錄,比第一條sql語句查找多了9W多條。所以,優化前的sql語句的關聯順序是MySQL優化器的選擇,可讓查詢進行更小的嵌套循環和回溯操做。MySQL經過選擇合適的關聯順序來讓查詢執行的成本儘量低,從新定義關聯的順序是優化器很重要的一部分功能。不過有時候,優化器給出的並非最優的關聯順序。這時能夠使用STRAIGHT_JOIN關鍵字重寫查詢,讓優化器按照你認爲的最優關聯順序執行。索引
從優化後的explain分析結果看出,news是驅動表,結果以news表的published_at字段進行排序,因此用上了索引,避免了Using temporary和Using filesort,天然而然的,查詢時間也降下來了。正如前面說的,mysql的優化器經過粗暴的小表驅動大表來選擇鏈接的順序,第一條sql語句掃描了161590行,第二條sql語句掃描了255440行,優化後的sql語句掃描的行數增長了。
結案陳詞:形成此次sql語句查詢耗時5s的緣由是,sql語句order by的字段不在mysql的優化器選在驅動表上,因此致使此次關聯查詢排序字段上的索引沒有被使用。所以,經過使用STRAIGHT_JOIN來強制制定關聯查詢的表順序,以達到優化的目的。可是,有時候咱們人爲地指定順序不必定比mysql的優化引擎準確,因此在使用STRAIGHT_JOIN的時候三思然後行。
本文連接:http://www.hcoding.com/?p=211
原創文章,轉載請註明:JC&hcoding.com
陸游
早歲那知世事艱,中原北望氣如山。
樓船夜雪瓜洲渡,鐵馬秋風大散關。
塞上長城空自許,鏡中衰鬢已先斑。
出師一表真名世,千載誰堪伯仲間。