1. 程式人生 > 其它 >MySQL基礎 - 基於成本的優化

MySQL基礎 - 基於成本的優化

在語句執行之前,MySQL查詢優化器會找出執行該語句所有可能的方案,對比找到成本最低的方案,該方案叫執行計劃

成本主要分為兩種:

  I/O成本:以為單位將資料和索引從磁碟載入到記憶體的成本,預設規定讀取一個頁面的成本是1.0CPU成本:

  從記憶體讀取記錄並檢測搜尋條件、對結果集進行排序等操作的成本,預設是0.2

單表查詢的成本

  

  基於成本的優化步驟:

  1. 根據搜尋條件找到所有可能用到的索引

  2. 計算使用使用不同索引方案執行查詢的代價(優先分析唯一二級索引

  3. 計算全表掃描的代價

  4. 對比各種方案代價,找到執行計劃

計算全表掃描的代價

全表掃描就是把聚集索引的中所有的記錄依次和所有條件比較(獲取最左側葉子節點,向右側遍歷比較),把符合條件的放入結果集,由於查詢成本=I/O成本+CPU成本

因此全表掃描代價與頁面數和表中記錄數相關MySQL設計者將為每個表都維護了一系列統計資訊通過show table status檢視:其中的Rows是行數,對於MyISAM是準確值,對InnoDB是估算值;Data_length是表佔用的儲存空間位元組數,對MyISAM是資料檔案大小,對InnoDB是聚集索引大小(等同於資料檔案)由於每個頁面預設16KB,因此可以算出頁面的數量。

例:Rows = 9693 , Data_length = 1589248 , 則聚簇索引頁面數量得:1589248/16/1024 = 97 ,

  IO成本估算:97 * 1.0 +1.1=98.1 公式解析:載入一個頁面的成本常數為1.0 ,後面的1.1是個微調值。

  CPU成本估算: 9693*02+1.0=1939.6 公式解析:0.2是訪問一條記錄所需的成本長數,後面的1.0是個微調值。

  總成本為: 1939.6+98.1 = 2037.7

計算不通索引執行查詢的代價

  1)使用不同索引執行查詢的代價

    對於使用二級索引+回表方式執行的查詢,在計算這種查詢的成本依賴兩方面資料:掃描區間數量和需要回表的記錄數。

    * 掃描區間數量:無論某個掃描區間的二級索引到底佔用了多少頁面,查詢優化器都認為讀取索引一個掃描區間的I/O成本與讀取一個頁面的I/O成本相同。一個掃描區間的I/O成本即為1 * 1.0 = 1;

    * 需要回表的記錄數:

      

a)分別找到區間最左記錄與最右記錄。

      b)如果區間最左記錄和區間最右記錄相隔不太遠(在Mysql5.7.22版本中 只要相隔不大於10個頁即可)就可以精準統計出 掃描區間的記錄數。

      PS:(資料頁PageHeader有一個PAGE_N_RECS屬性儲存了該頁面的記錄數,所以滿足這個不太遠的要求,直接遍歷這些頁面,把頁面的PAGE_N_RECS加起來就可以了)

      c) 設該掃描區間記錄數為 95條記錄, 讀取95條二級索引記錄需要的CPU成本則為 95 * 0.2 + 0.0.1,0.2為讀取一條記錄的成本常數,0.01是微調值。

    * 根據這些記錄的主鍵值到聚簇索引中執行回表操作:

      a) 執行回表操作的I/O成本相當於訪問一個頁面,所以回表操作的I/O成本為 95 * 1.0 = 95

    

    * 回表操作後得到完整的使用者記錄,然後再檢測其他搜尋條件是否成立。

      a) I/O成本 1.0 + 95 * 1.0 = 96 (掃描區間數量+預估二級索引記錄數量)

      b)CPU成本:95 * 0.2 + 0.01 + 95 * 0.2 = 38.01 (讀取二級索引記錄的成本+讀取病檢測回表操作做後聚簇索引記錄的成本)

    所以總成本為96+38.01=134.01

 通過 索引區間 作為查詢條件,MySQL底層會找到區間最左記錄和區間最右記錄,通過這兩個記錄向上回溯到 相同祖先節點 通過這些 內節點 的記錄數可以獲取區間最左記錄和區間最右記錄的 頁面數(準確值),然後從區間最左記錄向右讀取10個頁面,計算 平均 每個頁面的記錄數。最終通過頁面數X平均頁面記錄數得到索引區間的記錄數(估算值)這個過程叫:index dive MySQL也會把IN語句作為 單點區間 來計算成本,如果IN語句中的條件非常多,進行 index dive 效率就會非常低(通過show varibales like %dive%可以檢視IN語句引數限制值eq_range_index_dive_limit),因此當IN語句的引數超過限制值不會使用 index dive 而是使用 統計資料 來進行估算(如果使用IN語句卻沒有用到所引,可能是eq_range_index_dive_limit引數值設定太小,而使用了 統計資料 進行估算) 通過show index from <表名>;可以檢視索引的統計資料,其中Cardinality是索引列的基數(去重後記錄數的估算值) ​ 通過 show table status like '<表名>'可以查看錶空間的統計資料,其中的Rows是表中的記錄的估算值 ​ 一個值的重複次數≈Rows / Cardinality,得到 重複次數估算值 和 單點區間 的列相乘,就可以得到這些 單點區間 對應的記錄條數估算值了

Nice to see you all!