交代一下背景,這算是一次項目經驗吧,屬於公司一個已上線平臺的功能,這算是離職人員挖下的坑,隨着數據愈來愈多,本來的SQL查詢變得愈來愈慢,用戶體驗特別差,所以SQL優化任務交到了我手上。
這個SQL查詢關聯兩個數據表,一個是攻擊IP用戶表主要是記錄IP的信息,如第一次攻擊時間,地址,IP等等,一個是IP攻擊次數表主要是記錄天天IP攻擊次數。而需求是獲取某天攻擊IP信息和次數。(如下SQL語句測試均在測試服務器上上,正式服務器的性能好,查詢時間快很多。)php
查看錶的行數:
未優化前SQL語句爲:sql
SELECT attack_ip, country, province, city, line, info_update_time AS attack_time, sum( attack_count ) AS attack_times FROM `blacklist_attack_ip` INNER JOIN `blacklist_ip_count_date` ON `blacklist_attack_ip`.`attack_ip` = `blacklist_ip_count_date`.`ip` WHERE `attack_count` > 0 AND `date` BETWEEN '2017-10-13 00:00:00' AND '2017-10-13 23:59:59' GROUP BY `ip` LIMIT 10 OFFSET 1000
先EXPLAIN分析一下:
這裏看到索引是有的,可是IP攻擊次數表blacklist_ip_count_data也用上了臨時表。那麼這SQL不優化直接第一次執行須要多久(這裏強調第一次是由於MYSQL帶有緩存功能,執行過一次的一樣SQL,第二次會快不少。)
實際查詢時間爲300+秒,這徹底不能接受呀,這仍是沒有其餘搜索條件下的。
那麼咱們怎麼優化呢,索引既然走了,我嘗試一下避免臨時表,這時咱們先了解一下臨時表跟group by的使聯繫:緩存
查找了網上一些博客分析GROUP BY 與臨時表的關係 :
1. 若是GROUP BY 的列沒有索引,產生臨時表.
2. 若是GROUP BY時,SELECT的列不止GROUP BY列一個,而且GROUP BY的列不是主鍵 ,產生臨時表.
3. 若是GROUP BY的列有索引,ORDER BY的列沒索引.產生臨時表.
4. 若是GROUP BY的列和ORDER BY的列不同,即便都有索引也會產生臨時表.
5. 若是GROUP BY或ORDER BY的列不是來自JOIN語句第一個表.會產生臨時表.
6. 若是DISTINCT 和 ORDER BY的列沒有索引,產生臨時表.服務器
仔細按照上面分析一下,這SQL多是由於第二條致使的,blacklist_ip_count_date這個表的確主鍵不是IP,SELECT是多列的,那麼咱們試試單獨提出單表測試能不能避免臨時表:
很遺憾,並不能避免,可是咱們仔細看看這EXPLAIN 裏面的KEY 分析,用的索引是date單字段的索引。這好像就是致使了第一條的問題了,至關於GROUP BY沒有用索引。那麼咱們試試強制使用IP單字段的索引呢?
這裏看來的確是索引的問題,致使了臨時表啊,然而再看看ROWS的數量,原來的9W變成了1552W,這不是否是撿了芝麻掉了西瓜嗎?
這裏單列索引 避免了臨時表但是聯繫的行數又增長了,那麼咱們再試試複合索引呢?
因而建立attack_count、date、ip的複合索引index_Acount_date_ip
ROWS的行數770W並且仍是有臨時表,看來這複合索引也是不可取。
到此,避免臨時表方法失敗了,咱們得從其餘角度想一想如何優化。
其實,9W的臨時表並不算多,那麼爲何致使會這麼久的查詢呢?咱們想一想這沒優化的SQL的執行過程是怎麼樣的呢?markdown
網上搜索得知內聯表查詢通常的執行過程是:
一、執行FROM語句 二、執行ON過濾 三、添加外部行 四、執行where條件過濾 五、執行group by分組語句 六、執行having 七、select列表 八、執行distinct去重複數據 九、執行order by字句 十、執行limit字句
這裏得知,Mysql 是先執行內聯表而後再進行條件查詢的最後再分組,那麼想一想這SQL的條件查詢和分組都只是一個表的,內聯後數據就變得臃腫了,這時候再進行條件查詢和分組是否太吃虧了,咱們能夠嘗試一下提早進行分組和條件查詢,實現方法就是子查詢聯合內聯查詢。
這裏EXPLAIN看來,只是多了子查詢,ROWS和臨時表都沒有變化。那麼咱們看看實際的效果呢?
可見,取出來的數據徹底如出一轍,但是優化後效率從原來的330秒變成了0.28秒,這裏足足提高了1000多倍的速度。這也基本知足了咱們的優化需求。
估計到這裏,你猜這裏就是所有的優化方案?不不不,整個優化過程怎麼可能只是發現一個優化方案。還有其餘方案我會分開文章寫,具體請查看個人下一篇文章MYSQL一次千萬級連表查詢優化(二)。post
整個過程當中咱們得知,其實EXPLAIN有時候並不能指出你的SQL的全部問題,有一些隱藏問題必需要你本身思考,正如咱們這個例子,看起來臨時表是最大效率低的源頭,可是實際上9W的臨時表對MYSQL來講不足以掛齒的。咱們進行內聯查詢前,最好能限制連的表大小的條件都先用上了,同時儘可能讓條件查詢和分組執行的表儘可能小。感謝您們的閱讀,若是有更好的方案,歡迎留言交流!!!性能