概述:
交代一下背景,這算是一次項目經驗吧,屬於公司一個已上線平台的功能,這算是離職人員挖下的坑,隨着數據越來越多,原本的SQL查詢變得越來越慢,用戶體驗特別差,因此SQL優化任務交到了我手上。
這個SQL查詢關聯兩個數據表,一個是攻擊IP用戶表主要是記錄IP的信息,如第一次攻擊時間,地址,IP等等,一個是IP攻擊次數表主要是記錄每天IP攻擊次數。而需求是獲取某天攻擊IP信息和次數。(以下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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
先EXPLAIN分析一下:
這里看到索引是有的,但是IP攻擊次數表blacklist_ip_count_data也用上了臨時表。那么這SQL不優化直接第一次執行需要多久(這里強調第一次是因為MYSQL帶有緩存功能,執行過一次的同樣SQL,第二次會快很多。)
實際查詢時間為300+秒,這完全不能接受呀,這還是沒有其他搜索條件下的。
那么我們怎么優化呢,這里用的是內聯表查詢,大家都是知道子查詢完全是可以代替內聯表查詢的,只不過SQL語句復雜了不少,那么我們分析一下這SQL,兩個表分表提供了什么?
1、IP攻擊次數表blacklist_ip_count_data主要提供的指定時間條件查詢,攻擊次數條件查詢后的IP和每個IP符合條件下的具體攻擊次數。
2、攻擊IP用戶表blacklist_attack_ip主要是具體IP的信息,如第一次攻擊時間,地址,IP等等。
那么我們一步步來:
1、IP攻擊次數表blacklist_ip_count_data獲取符合時間條件和攻擊次數的IP並且以IP分組:
2、攻擊IP用戶表blacklist_attack_ip指定具體的IP獲取信息:
然后結合在一起:
可見,取出來的數據完全一模一樣,可是優化后效率從原來的330秒變成了0.28秒,這里足足提升了1000多倍的速度。這也基本滿足了我們的優化需求。
我們EXPLAIN了解一下情況: