分析一個執行時間很長的sql

背景

在查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也這么少嗎?


Q8執行計劃

Q43執行計劃

進行猜測

最后使用show profile 驗證一下

  1. 打開profile: set profiling = 1;
  2. 執行兩條sql語句
  3. 執行show profiles;
  4. 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;
將部分差異明顯的內容整理成表格,如下所示:

二者show profile主要的差異對比

結論:

  1. 可以看出,Q8 執行的主要耗時是statistics過程中,此過程中讀入了若干block 。
    先進行publish_time 的排序,然后回表根據where做檢索。當檢索的條數等于limit_count 時,檢索停止,返回數據。
    Q8會觸發mysql對limit的優化策略mysql5.6官方對limit的優化。所以在篩選到6條記錄后進行返回了。
  2. Q43先通過publisth_time進行排序,然后全表檢索,Q43每條記錄都不符合where的限定條件。耗時都在sending data階段?!緸槭裁床皇莈xecting階段?】
    該過程block_ops_in 高達1733k。后文估計了表內所有數據大約占 239k個block,這個數字遠小于1733k,顯然是有重復讀入的。

仍存的疑問:

  1. 官方對block_ops 解釋過于簡單,有沒有更詳細的說法。

  2. 為什么block_ops_in會重復讀入,與耗時成正比呢?對block_ops 的


    兩次執行Q8的對比

    當然,表格標黃處都與query耗時成正比,時間越長,cpu時間 上下文切換肯定會越高的。

  3. 為什么Q8會有block_ops_out?
    官網對block_ops_in 和block_ops_out的解釋
    The number of block input and output operations.
    ops是operation per second嗎?

  4. 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 會很小。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯系作者。

推薦閱讀更多精彩內容

  • 分析SQL執行帶來的開銷是優化SQL的重要手段。在MySQL數據庫中,可以通過配置profiling參數來啟用SQ...
    張偉科閱讀 1,112評論 0 1
  • ORA-00001: 違反唯一約束條件 (.) 錯誤說明:當在唯一索引所對應的列上鍵入重復值時,會觸發此異常。 O...
    我想起個好名字閱讀 5,429評論 0 9
  • Swift1> Swift和OC的區別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,136評論 1 32
  • 1.設計模式是什么? 你知道哪些設計模式,并簡要敘述?設計模式是一種編碼經驗,就是用比較成熟的邏輯去處理某一種類型...
    龍飝閱讀 2,187評論 0 12
  • 勞累一天的鳥,睡了 雄鳥在巢邊的枝上守護,警惕 雌鳥在巢里低垂扇面的翅膀 裹胸幾個雛鳥的低鳴,并非寒冷 頭鉆出母親...
    忠志_3d7b閱讀 202評論 1 2