select count(1) as maxsize from person_dt; //獲取最大條數
https://blog.csdn.net/weixin_26824207/article/details/113309821
作者:何甜甜在嗎
來源:juejin.im/post/5bcc2935f265da0ac66987c9
(一)慢sql一
問題發現
將應用發布到生產環境后,前端頁面請求后台API返回數據,發現至少需要6s。查看到慢sql:

慢sql定位.png
復現慢sql
執行sql:
select count(*) from sync_block_data
where unix_timestamp(sync_dt) >= 1539101010
AND unix_timestamp(sync_dt) <= 1539705810
查看耗時:

慢查詢耗時.png
一共耗時為2658ms 查看執行計划:
explain select count(*) from sync_block_data
where unix_timestamp(sync_dt) >= 1539101010
AND unix_timestamp(sync_dt) <= 1539705810
執行計划結果:

慢查詢執行計划.png
優化慢sql一
sync_dt的類型為datetime類型。換另外一種sql寫法,直接通過比較日期而不是通過時間戳進行比較。將sql中的時間戳轉化為日期,分別為2018-10-10 00:03:30和2018-10-17 00:03:30 執行sql:
select count(*) from sync_block_data
where sync_dt >= "2018-10-10 00:03:30"
AND sync_dt <= "2018-10-17 00:03:30"
查看耗時:

快查詢耗時.png
一共耗時419毫秒,和慢查詢相比速度提升六倍多 查看執行計划:
explain select count(*) from sync_block_data
where sync_dt >= "2018-10-10 00:03:30"
AND sync_dt <= "2018-10-17 00:03:30"
執行計划結果:

快查詢執行計划.png
訪問頁面,優化完成后請求時間平均為900毫秒

執行計划中慢查詢和快查詢唯一的區別就是type不一樣:慢查詢中type為index,快查詢中type為range。
優化慢查詢二
這條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類型字段sync_dt_long存儲sync_dt的毫秒值,並在sync_dt_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
explain使用介紹
通過explain,可以查看sql語句的執行情況(比如查詢的表,使用的索引以及mysql在表中找到所需行的方式等) 用explain查詢mysql查詢計划的輸出參數有:


重點關注type,type類型的不同竟然導致性能差六倍!!!

type顯示的是訪問類型,是較為重要的一個指標,結果值從好到壞依次是:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL ,一般來說,得保證查詢至少達到range級別,最好能達到ref。


出現慢查詢的原因
在where子句中使用了函數操作 出現慢查詢的sql語句中使用了unix_timestamp函數統計出自'1970-01-01 00:00:00'的到當前時間的秒數差。導致索引全掃描統計出近七天的數據量的
解決方案
盡量避免在where子句中對字段進行函數操作,這將導致存儲引擎放棄使用索引而進行全表掃描。對於需要計算的值最好通過程序計算好傳入而不是在sql語句中做計算,比如這個sql中我們將當前的日期和七天前的日期計算好傳入
后記
這個問題當時在測試環境沒有發現,測試環境的請求速度還是可以的。沒有被發現可以歸結為數據量。生產數據量為百萬級別,測試環境數據量為萬級,數據量差50倍,數據量的增大把慢查詢的問題也放大了。
(二)慢sql二
因為線上出現了很明顯的請求響應慢的問題,又去看了項目中的其他sql,發現還有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
優化慢sql一
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的情況, 查詢效率不高, 仍需要進行優化,這里出現臨時表的原因是數據量過大使用了臨時表進行分組運算
優化慢sql二
慢查詢的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。完美
就這樣第一次經歷了真正的慢查詢以及慢查詢優化,終於理論和實踐相結合了