mysql查詢優化--臨時表和文件排序(Using temporary; Using filesort問題解決)


先看一段sql:

  1. <span style="font-size:18px;">SELECT  
  2.     *  
  3. FROM  
  4.     rank_user AS rankUser  
  5. LEFT JOIN rank_user_level AS userLevel ON rankUser.id = userLevel.user_id  
  6. LEFT JOIN rank_product AS product ON userLevel.new_level = product.level_id  
  7. LEFT JOIN rank_product_fee AS fee ON userLevel.fee_id = fee.fee_id  
  8. LEFT JOIN rank_user_login_stat AS userLoginInfo ON rankUser.id = userLoginInfo.user_id  
  9. ORDER BY  
  10.      rankUser.create_time DESC  
  11. LIMIT 10 OFFSET 0</span>  



介紹一下這段sql的表的構成:一張主表:rank_user;兩張跟rank_user直接關聯(多張表通過同一字段最好是主鍵進行關聯)的表:rank_user_level ,rank_user_login_stat ;兩張跟rank_user非直接關聯的表:rank_product ,rank_product_fee 。這段sql看似簡單,但是執行時間卻很長,我們來看一下執行計划:


執行時間1.45s,可以看到,這段不僅僅掃描全表,而且使用了臨時表,進行了文件排序。

為了找到原因,我們把排序去掉看一下:

  1. SELECT  
  2.     *  
  3. FROM  
  4.     rank_user AS rankUser  
  5. LEFT JOIN rank_user_level AS userLevel ON rankUser.id = userLevel.user_id  
  6. LEFT JOIN rank_product AS product ON userLevel.new_level = product.level_id  
  7. LEFT JOIN rank_product_fee AS fee ON userLevel.fee_id = fee.fee_id  
  8. LEFT JOIN rank_user_login_stat AS userLoginInfo ON rankUser.id = userLoginInfo.user_id  
  9. -- ORDER BY  
  10.     -- rankUser.create_time DESC  
  11. LIMIT 10 OFFSET 0  



執行時間0.015s,掃描行數67452,果然是排序惹的禍。但是僅僅是排序惹的禍嗎?別忘了這里有兩張非直接關聯的表,這樣的查詢,如果有查詢條件或者排序分組的時候往往都需要創建臨時表(這個沒有辦法,想想也知道)。為了驗證這個觀點,我們把兩張非直接關聯的表去掉看一下:

  1. SELECT  
  2.     *  
  3. FROM  
  4.     rank_user AS rankUser  
  5. LEFT JOIN rank_user_level AS userLevel ON rankUser.id = userLevel.user_id  
  6. -- LEFT JOIN rank_product AS product ON userLevel.new_level = product.level_id  
  7. -- LEFT JOIN rank_product_fee AS fee ON userLevel.fee_id = fee.fee_id  
  8. LEFT JOIN rank_user_login_stat AS userLoginInfo ON rankUser.id = userLoginInfo.user_id  
  9. ORDER BY  
  10.     rankUser.create_time DESC  
  11. LIMIT 10 OFFSET 0  

執行時間0.003s,掃描行數10,屌爆了有木有,mysql多表直接關聯在沒有其他篩選條件的情況下,查詢速度大大提升,而且排序可以使用create_time這個索引,直接取到前十條。


到了這里,我想大家應該已經明白第一條sql查詢時間很長的原因了:多表非直接關聯的前提下還要排序。mysql查詢往往最需要優化的地方就是臨時表和文件排序了。這里總結一下教訓:


1.mysql查詢存在直接關聯和非直接關聯的問題,這兩種查詢效率差別很大;

2.mysql排序盡量使用索引;

3.mysql多表關聯left join其他表的時候,如果以其他表的字段作為查詢條件都會產生臨時表;

4.mysql在非直接關聯的基礎上進行排序會很慢,需要進行優化;



知道了問題,我們就好優化了,這里我給出了兩種方案:

第一種(子查詢,適合子查詢部分不作為查詢條件):

  1. SELECT  
  2.             rankUser.id, rankUser.qq, rankUser.phone, rankUser.regip, rankUser.channel, rankUser.create_time, rankUser.qudao_key, rankUser.qq_openid, rankUser.wechat_openid,  
  3.             userLevel.recommend_count,userLevel.end_time,userLevel.new_level,userLevel.`level`,userLevel.new_recommend_count,userLevel.`is_limited`,  
  4.             (case when userLevel.new_level > 1 then 1 else 0 end) is_official_user,  
  5.             (select product_name from rank_product where level_id = userLevel.new_level) product_name,  
  6.             (select period from rank_product_fee where fee_id = userLevel.fee_id) period,  
  7.             userLoginInfo.last_login, userLoginInfo.login_count, userLoginInfo.login_seconds  
  8.         FROM rank_user AS rankUser  
  9.         LEFT JOIN rank_user_level as userLevel on userLevel.user_id=rankUser.id  
  10.         LEFT JOIN rank_user_login_stat as userLoginInfo ON rankUser.id = userLoginInfo.user_id  
  11. ORDER BY  
  12.     rankUser.create_time DESC  
  13. LIMIT 10 OFFSET 0  


第二種(非直接關聯轉變成直接關聯,這個要根據業務來定,我這里rank_product和rank_product_fee兩張表只是為了查詢rank_user_level表中的產品和產品費用信息,而rank_user_level是一張直接關聯的表,故這里可以先將這三張表進行合並,然后再和rank_user表進行聯合查詢):

  1. SELECT  
  2.     *  
  3. FROM  
  4.     rank_user AS rankUser  
  5. LEFT JOIN (  
  6.         select   
  7.         l.*,p.product_name,f.period   
  8.         from   
  9.         rank_user_level l,rank_product p,rank_product_fee f  
  10.         where   
  11.         l.new_level = p.level_id   
  12.         and l.fee_id = f.fee_id  
  13. AS userLevel ON rankUser.id = userLevel.user_id  
  14. LEFT JOIN rank_user_login_stat AS userLoginInfo ON rankUser.id = userLoginInfo.user_id  
  15. ORDER BY  
  16.     rankUser.create_time DESC  
  17. LIMIT 10 OFFSET 0  




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM