背景
在查bug時發現有兩個相似sql查詢速度相差巨大:
SELECT * FROM news_basic WHERE category =43 AND status in (1,7) AND source in (11,12,13)
ORDER BY publish_time DESC LIMIT 6;
SELECT * FROM news_basic WHERE category =8 AND status in (1,7) AND source in (11,12,13)
ORDER BY publish_time DESC LIMIT 6;
區別僅在于 category 一個是43,一個是8。(后面方便闡述稱第一個sql為Q43,第二個sql為Q8。)
Q43的query速度:0.00347175秒;Q8是21.150秒。
數據庫版本:5.6.28
當然,在實際業務中,WHERE status in (1,7) AND source in (11,12,13) 的數據子集的category 取值分布在[10,1000]。所以,Q8的結果集必定是空的,從業務上講是個不符合業務的sql。
但這兩個sql為什么速度差這么多呢?
首先對比下數據庫中兩個category的記錄數,數據量差距并不大。排除兩category數據量相差過大造成的影響。
再對比下執行計劃 EXPLAIN
很奇怪,兩sql的執行計劃一致。
查詢過程先走publish_time的索引再回表,沒有疑問。只是rows估計掃過行數都是24,Q8也這么少嗎?
進行猜測
最后使用show profile 驗證一下
- 打開profile: set profiling = 1;
- 執行兩條sql語句
- 執行show profiles;
-
show profile for query N; (這里N=1,2).
結果如圖所示(左側Q8,右側Q43)
左側Q8,右側Q43
時間都消耗在sending data上了,執行器耗時巨長。
注:Sending data 并不一定是指“正在發送數據”,而可能是處于執行器過程中的任意階段.(林曉斌 MySQL實戰45講,33講)
所以,雖然Q45結果集是空,但sending data仍可能很長。
繼續深入下: 執行show profile ALL for query N;
將部分差異明顯的內容整理成表格,如下所示:
結論:
- 可以看出,Q8 執行的主要耗時是statistics過程中,此過程中讀入了若干block 。
先進行publish_time 的排序,然后回表根據where做檢索。當檢索的條數等于limit_count 時,檢索停止,返回數據。
Q8會觸發mysql對limit的優化策略mysql5.6官方對limit的優化。所以在篩選到6條記錄后進行返回了。 - Q43先通過publisth_time進行排序,然后全表檢索,Q43每條記錄都不符合where的限定條件。耗時都在sending data階段?!緸槭裁床皇莈xecting階段?】
該過程block_ops_in 高達1733k。后文估計了表內所有數據大約占 239k個block,這個數字遠小于1733k,顯然是有重復讀入的。
仍存的疑問:
官方對block_ops 解釋過于簡單,有沒有更詳細的說法。
-
為什么block_ops_in會重復讀入,與耗時成正比呢?對block_ops 的
兩次執行Q8的對比
當然,表格標黃處都與query耗時成正比,時間越長,cpu時間 上下文切換肯定會越高的。
為什么Q8會有block_ops_out?
官網對block_ops_in 和block_ops_out的解釋
The number of block input and output operations.
ops是operation per second嗎?execting和sending data的區別。
估算表內數據占多少block
page 和 block 是一個概念。庫中pagesize是默認值 16384,也就是16k。news_basic表數據所占共3.65GB。
很粗略地估計一下(如果表中有長字段,那么每行的size會更小,實際頁數會更少)。
其他的發現
navicat的“概況”
navicat在執行完一批次查詢后有個“概況”標簽,與show profile類似,但是實際結果二者不太相同。
navicat會執行sql進行統計:
SELECT STATE AS `Status`, ROUND(SUM(DURATION),7) AS `Duration`, CONCAT(ROUND(SUM(DURATION)/0.002964*100,3), '') AS `Percentage`
FROM INFORMATION_SCHEMA.PROFILING
WHERE QUERY_ID=102
GROUP BY SEQ, STATE
ORDER BY SEQ
navicat分析Q8,大量的時間在execting下,sending data 會很小。