以前看了餓了麼團隊寫的一篇博客:等等!這兩個 Spring-RabbitMQ 的坑咱們已經替你踩了。深受啓發,必定要取個能吸引讀者眼球的標題,固然除了響噹噹的標題之外,內容也要是乾貨。爲何會想取這樣一個標題,由於看了理論上的慢查詢優化,今天!!!終於在生產上實戰了前端
將應用發佈到生產環境後,前端頁面請求後臺API返回數據,發現至少須要6s。查看到慢sql:java
執行sql:mysql
select count(*) from sync\_block\_datawhere unix\_timestamp(sync\_dt) >= 1539101010AND unix\_timestamp(sync\_dt) <= 1539705810
查看耗時:面試
一共耗時爲2658ms 查看執行計劃:sql
explain select count(*) from sync\_block\_datawhere unix\_timestamp(sync\_dt) >= 1539101010AND unix\_timestamp(sync\_dt) <= 1539705810
執行計劃結果:後端
sync_dt的類型爲datetime類型。換另一種sql寫法,直接經過比較日期而不是經過時間戳進行比較。將sql中的時間戳轉化爲日期,分別爲2018-10-10 00:03:30和2018-10-17 00:03:30 執行sql:緩存
select count(*) from sync\_block\_datawhere sync\_dt >= "2018-10-10 00:03:30"AND sync\_dt <= "2018-10-17 00:03:30"
查看耗時:微信
一共耗時419毫秒,和慢查詢相比速度提高六倍多 查看執行計劃:多線程
explain select count(*) from sync\_block\_datawhere sync\_dt >= "2018-10-10 00:03:30"AND sync\_dt <= "2018-10-17 00:03:30"
執行計劃結果:架構
訪問頁面,優化完成後請求時間平均爲900毫秒
執行計劃中慢查詢和快查詢惟一的區別就是type不同:慢查詢中type爲index,快查詢中type爲range。整編:微信公衆號,搜雲庫技術團隊,ID:souyunku
這條sql的業務邏輯爲統計出最近七天該表的數據量,能夠去掉右邊的小於等於 執行sql:
select count(*) from sync\_block\_datawhere sync_dt >= "2018-10-10 00:03:30"
查看耗時:
一共耗時275毫秒,又將查詢時間減小了一半 查看執行計劃:
explain select count(*) from sync\_block\_datawhere sync_dt >= "2018-10-10 00:03:30"
執行計劃結果:
type還是range。可是經過少比較一次將查詢速度提升一倍
新建一個bigint類型字段syncdtlong存儲syncdt的毫秒值,並在syncdt_long字段上創建索引 測試環境下:優化慢查詢二sql
select count(*) from copy\_sync\_block\_datawhere sync\_dt >="2018-10-10 13:15:02"
耗時爲34毫秒 優化慢查詢三sql
select count(*) from copy\_sync\_block\_datawhere sync\_dt_long >= 1539148502916
耗時爲22毫秒 測試環境中速度提高10毫秒左右
優化慢查詢三sql測試小結:在InnoDB存儲引擎下,比較bigint的效率高於datetime 完成三步優化之後生產環境中請求耗時:
速度又快了200毫秒左右。經過給查詢的數據加10s緩存,響應速度最快平均爲20ms
id: 執行編號,標識select所屬的行。若是在語句中沒子查詢或關聯查詢,只有惟一的select,每行都將顯示1。不然,內層的select語句通常會順序編號,對應於其在原始語句中的位置
select_type: 顯示本行是簡單或複雜select。若是查詢有任何複雜的子查詢,則最外層標記爲PRIMARY(DERIVED、UNION、UNION RESUlT)
table: 訪問引用哪一個表(引用某個查詢,如「derived3」)
type: 數據訪問/讀取操做類型(ALL、index、range、ref、eq_ref、const/system、NULL)
possible_keys: 揭示哪一些索引可能有利於高效的查找
key: 顯示mysql決定採用哪一個索引來優化查詢
key_len: 顯示mysql在索引裏使用的字節數
ref: 顯示了以前的表在key列記錄的索引中查找值所用的列或常量
rows: 爲了找到所需的行而須要讀取的行數,估算值,不精確。經過把全部rows列值相乘,可粗略估算整個查詢會檢查的行數。整編:微信公衆號,搜雲庫技術團隊,ID:souyunku
Extra: 額外信息,如using index、filesort等
重點關注type,type類型的不一樣居然致使性能差六倍!!!
type顯示的是訪問類型,是較爲重要的一個指標,結果值從好到壞依次是:system > const > eqref > ref > fulltext > refornull > indexmerge > uniquesubquery > indexsubquery > range > index > ALL ,通常來講,得保證查詢至少達到range級別,最好能達到ref。
All: 最壞的狀況,全表掃描
index: 和全表掃描同樣。只是掃描表的時候按照索引次序進行而不是行。主要優勢就是避免了排序, 可是開銷仍然很是大。如在Extra列看到Using index,說明正在使用覆蓋索引,只掃描索引的數據,它比按索引次序全表掃描的開銷要小不少
range: 範圍掃描,一個有限制的索引掃描。key 列顯示使用了哪一個索引。當使用=、 <>、>、>=、<、<=、IS NULL、<=>、BETWEEN 或者 IN 操做符,用常量比較關鍵字列時,可使用 range |
ref: 一種索引訪問,它返回全部匹配某個單個值的行。此類索引訪問只有當使用非惟一性索引或惟一性索引非惟一性前綴時纔會發生。這個類型跟eq_ref不一樣的是,它用在關聯操做只使用了索引的最左前綴,或者索引不是UNIQUE和PRIMARY KEY。ref能夠用於使用=或<=>操做符的帶索引的列。
eq_ref: 最多隻返回一條符合條件的記錄。使用惟一性索引或主鍵查找時會發生 (高效)
const: 當肯定最多隻會有一行匹配的時候,MySQL優化器會在查詢前讀取它並且只讀取一次,所以很是快。當主鍵放入where子句時,mysql把這個查詢轉爲一個常量(高效)
system: 這是const鏈接類型的一種特例,表僅有一行知足條件。
Null: 意味說mysql能在優化階段分解查詢語句,在執行階段甚至用不到訪問表或索引(高效)
在where子句中使用了函數操做 出現慢查詢的sql語句中使用了unix_timestamp函數統計出自'1970-01-01 00:00:00'的到當前時間的秒數差。致使索引全掃描統計出近七天的數據量的
儘可能避免在where子句中對字段進行函數操做,這將致使存儲引擎放棄使用索引而進行全表掃描。對於須要計算的值最好經過程序計算好傳入而不是在sql語句中作計算,好比這個sql中咱們將當前的日期和七天前的日期計算好傳入
這個問題當時在測試環境沒有發現,測試環境的請求速度仍是能夠的。沒有被發現能夠歸結爲數據量。生產數據量爲百萬級別,測試環境數據量爲萬級,數據量差50倍,數據量的增大把慢查詢的問題也放大了。整編:微信公衆號,搜雲庫技術團隊,ID:souyunku
由於線上出現了很明顯的請求響應慢的問題,又去看了項目中的其餘sql,發現還有sql執行的效率比較低
執行sql
select FROM\_UNIXTIME(copyright\_apply\_time/1000,'%Y-%m-%d') point,count(1) numsfrom resource\_info where copyright\_apply\_time >= 1539336488355 and copyright\_apply\_time <= 1539941288355 group by point
查看耗時:
耗時爲1123毫秒 查看執行計劃:
explain select FROM\_UNIXTIME(copyright\_apply\_time/1000,'%Y-%m-%d') point,count(1) numsfrom resource\_info where copyright\_apply\_time >= 1539336488355 and copyright\_apply\_time <= 1539941288355 group by point
執行計劃結果:
索引是命中了,可是extra字段中出現了Using temporary和Using filesort
group by實質是先排序後分組,也就是分組以前必排序。經過分組的時候禁止排序優化sql 執行sql:
select FROM\_UNIXTIME(copyright\_apply\_time/1000,'%Y-%m-%d') point,count(1) numsfrom resource\_info where copyright\_apply\_time >= 1539336488355 and copyright\_apply\_time <= 1539941288355 group by point order by null
查看耗時:
一共耗時1068毫秒,提升100毫秒左右,效果並非特別明顯 查看執行計劃:
extra字段已經沒有Using filesort了,filesort表示經過對返回數據進行排序。全部不是經過索引直接返回排序結果的排序都是FileSort排序,說明優化後經過索引直接返回排序結果 Using temporary依然存在,出現Using temporary表示查詢有使用臨時表, 通常出現於排序, 分組和多表join的狀況, 查詢效率不高, 仍須要進行優化,這裏出現臨時表的緣由是數據量過大使用了臨時表進行分組運算。整編:微信公衆號,搜雲庫技術團隊,ID:souyunku
慢查詢的sql業務邏輯爲根據時間段分類統計出條件範圍內各個時間段的數量 好比給定的條件範圍爲2018-10-20~2018-10-27的時間戳,這條sql就會統計出2018-10-20~2018-10-27天天的數據增量。如今優化成一天一天查,分別查七次數據,去掉分組操做
select FROM\_UNIXTIME(copyright\_apply\_time/1000,'%Y-%m-%d') point,count(1) numsfrom resource\_info where copyright\_apply\_time >= 1539855067355 and copyright\_apply\_time <= 1539941467355
查看耗時:
耗時爲38毫秒,即便查7次所用時間也比1123毫秒少 查看執行計劃:
extra字段中和慢查詢的extra相比少了Using temporary和Using filesort。完美就這樣第一次經歷了真正的慢查詢以及慢查詢優化,終於理論和實踐相結合了
做者:何甜甜在嗎
https://juejin.im/post/5bcc29...
推薦去個人博客閱讀更多:
2.Spring MVC、Spring Boot、Spring Cloud 系列教程
3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程
以爲不錯,別忘了點贊+轉發哦!