1. 問題描述:早上剛來上班,業務部門同事反應管理后台無法登錄
2. 問題排查定位
2.1 服務器排查
a. 接口是否可以調通:首先自己登陸后台,發現時好時壞,偶爾接口返回【系統忙】。我們系統接口異常調不通會返回系統忙
b. 服務是否死掉或者假死:連接服務器->查看Java進程,服務正常;排除假死:重啟該服務,接口依然調不通
c. 服務器各項指標:登錄阿里雲->雲服務器ECS->監控,查看CPU、內存、系統負載、雲盤iops、網絡帶寬、連接數,各項指標使用率均在正常范圍
2.2 數據庫排查
a. 數據庫各項指標:登錄阿里雲->雲數據庫RDS->監控與報警,查看CPU、內存、磁盤空間、iops、網絡帶寬、連接總數,其它指標正常,唯獨CPU使用率達到100%!
b.一鍵診斷:登錄阿里雲->雲數據庫RDS->自治服務->一鍵診斷,發現CPU使用率偏高,出現了異常慢SQL
c. 慢日志:日志管理 -> 慢日志明細 ,發現有SQL語句耗時7~8分鍾,甚至20~30分鍾。一條SQL語句執行20多分鍾!!!
d. sql分析:
SELECT you.nick_name,you.mobile,ycu.createtime,ycu.id,ycu.ym_userid, ycu.`status`,cir.coupon_name,cir.use_price,if(ycu.status = '1',ycul.createtime,'') as usedtime,cir.coupon_type as couponType, cir.price,cir.province,ycu.end_time from ym_coupon_user ycu LEFT JOIN ym_org_user you on ycu.ym_userid = you.id LEFT JOIN coupon_issue_record cir on ycu.coupon_issue_record_id = cir.id LEFT JOIN ym_coupon_used_log ycul on ycu.id = ycul.ym_coupon_user_id where 1=1 and you.platform_code ='lngpt' order by ycu.createtime desc limit 10
可以看出是四表聯查,如果有數據量比較大的表,然后關聯字段又沒有索引將會非常耗時
用explain分析,即在SQL語句前加上關鍵字 EXPLAIN然后執行:
可以看出,4個表中主表全盤掃描,第2、3表關聯字段有唯一索引只掃描一行,但是第4張表也進行了全盤掃描。上圖是測試環境,數據量較小。
線上數據表1和表4分別由10多萬條和4萬多條!然后關聯字段沒有加索引,導致查詢異常慢。
給表4的關聯字段添加索引(ym_coupon_user_id) 后執行EXPLAIN:
可以看出,表4(ycul)也用上了索引,只取出了一行。到此問題得以解決,一條查詢語句耗時由20多分鍾優化到0.16秒~
3. 總結:多表關聯查詢,如果數據量較大查詢太慢,被驅動表的關聯字段要加索引。
SQL語句剖析:
對比加索引前后的 EXPLAIN 結果分析:
1. ycu表在ycul表未加索引前使用了臨時表 (using temporary)
2. ycul表未加索引是:Using where; Using join buffer (Block Nested Loop),加了索引后是:Using where
關鍵字:using temporary、Using join buffer (Block Nested Loop)、Using filesort
下面分別進行解析:
問題1.為啥會出現臨時表
答案:用非驅動表字段排序會出現臨時表
關於左關聯查詢:首先分驅動表和被驅動表,先去驅動表查詢數據,然后以此數據為基准去查詢被驅動表。
驅動表和被驅動表的選擇受MySQL優化器的控制,這個比較復雜:
a、沒其它條件的話,選擇結果集小的作為驅動表
b、優先以order by字段所在的表作為驅動表,即時該表結果集較大
c、即使符合上面的條件,優化器還會優先選擇關聯字段有索引的表作為 被驅動表
d、都有索引,則不一定會選擇order by字段的表作為驅動表,此時如果數據量差距大選擇數據量小的為驅動表,如果數據量差距不明顯則選擇order by字段的表為驅動表
**** 到此,上面那條SQL語句就可以明白了,我們再分析下 EXPALIN ,為了方便查看我把SQL語句和EXPLAIN再復制一遍:
SELECT you.nick_name,you.mobile,ycu.createtime,ycu.id,ycu.ym_userid, ycu.`status`,cir.coupon_name,cir.use_price,if(ycu.status = '1',ycul.createtime,'') as usedtime,cir.coupon_type as couponType, cir.price,cir.province,ycu.end_time from ym_coupon_user ycu LEFT JOIN ym_org_user you on ycu.ym_userid = you.id LEFT JOIN coupon_issue_record cir on ycu.coupon_issue_record_id = cir.id LEFT JOIN ym_coupon_used_log ycul on ycu.id = ycul.ym_coupon_user_id where 1=1 and you.platform_code ='lngpt' order by ycu.createtime desc limit 10

首先,當ycul表關聯字段沒加索引時,表ycu和表ycul都沒有索引,雖然order by ycu.createtime,但ycu和ycul數據量差別很大,所以小表ycul被優化器選為驅動表。
而此時order by ycu.createtime,即排序字段在非驅動表上,迫使MySQL使用了臨時表,結果可想而知。
當給ycul的ym_coupon_user_id加上索引,則優化器選擇了ycu為主表,order by字段在主表,就沒有臨時表了。
問題2. 什么時候會使用 join buffer
…………待續
問題3. 何時出現 Using filesort
當Query中包含 order by 操作,而且無法利用索引完成的排序操作稱為“文件排序”