MySQL學習(8)基於成本的優化

来源:https://www.cnblogs.com/haleyeung/archive/2023/10/25/17787401.html
-Advertisement-
Play Games

什麼是成本 MySQL中一條SQL語句的執行成本包含兩個部分: I/O成本:從磁碟中載入數據(頁)到記憶體的的過程中消耗的時間稱為I/O成本。 CPU成本:讀取記錄以及檢測記錄是否滿足搜索條件、對結果集進行排序等操作,消耗的時間稱為CPU成本。 MySQL預設規定讀取一個頁面的I/O成本是1.0,讀取 ...


什麼是成本

MySQL中一條SQL語句的執行成本包含兩個部分:

  • I/O成本:從磁碟中載入數據(頁)到記憶體的的過程中消耗的時間稱為I/O成本。

  • CPU成本:讀取記錄以及檢測記錄是否滿足搜索條件、對結果集進行排序等操作,消耗的時間稱為CPU成本。

MySQL預設規定讀取一個頁面的I/O成本是1.0,讀取及檢測一條記錄的CPU成本是0.2。

單表查詢的成本

MySQL Server接收到客戶端發過來的SQL語句後,經歷過查找緩存(如有)和語法解析後,MySQL的優化器會找出所有可能用來執行這條語句的方案,並對比這些方案後選擇一條成本最低的方案,這個成本最低的方案就是執行計劃,在此期間會訪問少量的數據。然後才會跳用存儲引擎的結構真正地執行查詢。這個過程總結一下:

  1. 根據搜索條件,找出所有可能使用的索引。

  2. 計算全表掃描的代價。

  3. 計算使用不同索引執行查詢的代價。

  4. 對比各種執行方案的代價,找出成本最低的方案。

SELECT
  *
FROM
  single_table
WHERE
  key1 IN ('a', 'b', 'c')
  AND key2 > 10
  AND key2 < 1000
  AND key3 > key2
  AND key_part1 LIKE '%a%'
  AND common_field LIKE 'a%';

 

找出所有可能使用的索引possible keys

根據搜索條件,只要索引列和常數使用=、<=>、IN、NOT IN、IS NULL、IS NOT NULL、>、<、>=、<=、BETWEEN、!=、<>或者LIKE,就會形成掃描區間,這些搜索條件都可能使用到索引,一個查詢中可能使用到的索引統稱為possible keys

  • key1 IN ('a', 'b', 'c')使用了二級索引idx_key1。

  • key2 > 10 AND key2 < 1000使用了唯一二級索引uk_key2。

  • key3 > key2未與常數進行比較,無法形成合適的掃描區間。

  • key_part1 LIKE '%a%'使用LIKE時無法匹配開頭,無法形成合適的掃描區間。

  • common_field列未建立索引。

這條語句的possible keys為idx_key1、uk_key2。

計算全表掃描的代價

全表掃描的成本需要兩個參數:

  • 聚簇索引占用的頁面數

  • 該表中的記錄數

通過SHOW TABLE STATUS語句查看表的統計信息

SHOW TABLE STATUS LIKE 'single_table';

 

image-20231024150724964

  • Rows:對於InnoDB存儲引擎,這個值表示表中記錄條數的估值。

  • Data_length:表示表占用的存儲空間位元組數,對於InnoDB來說,相當於聚簇索引占用的存儲空間大小。

Data_length = 聚簇索引的頁面數量 ✖️ 每個頁面的大小

表的預設大小為16KB,可以推導出聚簇索引的頁面數量:

聚簇索引的頁面數量 = 1589248 ➗ 16 ➗ 1024 = 97

I/O成本:頁面數量為97,沒載入一個頁的成本為1.0,總共97 ✖️ 1.0 + 1.1 = 98.1,其中1.1為內部微調值。

CPU成本:讀取一條記錄的成本為0.2,總共9895 ✖️ 0.2 + 1.0 = 1939.6,其中1.0為內部微調值。

聚簇索引包含記錄和目錄項,只有葉子節點才是記錄,但是計算成本時,直接將所有頁面都計算進入。

 

計算使用不同索引的執行查詢的代價

MySQL優化器優先分析使用唯一二級索引的成本,再分析使用普通二級索引的成本。

分析步驟:

  1. 掃描區間數量

  2. 需要回表的記錄數

無論掃描區間的二級索引占用了多少頁面,優化器都認為讀取索引的一個掃描區間的I/O成本與讀取一個頁面的I/O成本是相同的,也就是幾個掃描區間,就有幾個1.0。

<a name="index fanout"></a>

優化器需要計算二級索引的某個掃描區間包含多少條記錄,才能知道有多少條記錄需要執行回表。1️⃣根據其中一個訪問條件找到滿足條件的第一條記錄,也稱為區間最左記錄;2️⃣根據條件從索引中找到最後一條記錄,也稱為區間最右記錄;3️⃣如果去見最左記錄和區間最右記錄相隔不是很遠(不大於10個頁),就可以精確統計出滿足條件的二級索引記錄的條數。

如果這些頁面相隔不太遠,可以通過INDEX頁中Page Header部分的PAGE_N_RECS屬性知道該頁有多少記錄,遍歷這些頁,將PAGE_N_RECS相加即可。如果頁面很多,就從區間最左記錄向右讀10個頁面,計算每個頁平均記錄數量,然後乘以區間頁數得出估算數值。父節點中每一個目錄項記錄對應的是一頁,只需要找出多少個目錄項,就知道區間有多少的頁。此時可以知道區間有多少條記錄,讀取這些記錄所需CPU成本為記錄數✖️0.2。

根據這些記錄的主鍵到聚簇索引進行回表,每次回表操作相當於訪問一個頁,也就是說二級索引掃描區間中有多少條記錄,就需要進行多少次回表,也就是需要進行多少次頁面I/O。I/O成本為二級索引得到的記錄數✖️1.0。

回表後,再讀取並判斷其他條件,需要消耗CPU成本記錄數量✖️0.2。

註意,實際運行的時候,第一次計算成本時,沒有加上回表後聚簇索引記錄的CPU成本,如果這個方案的執行成本較低,會重新計算一遍這個方案的成本,並把回表後聚簇索引的CPU成本加上。

當多個索引列使用AND連接,如果為單點區間可以使用索引Intersection合併;當使用OR連接,單點區間可以使用Union合併,若不是,可以使用Sort-Unino合併。

根據統計數據計算成本

當查詢語句使用IN形成很多很多單點掃描區間

SELECT * FROM single_table WHERE key1 IN ('aaa', 'aab', 'aac', 'aad'...'zzz');

 

由於搜索條件涉及key1列,可以使用idx_key1索引,但是idx_key1不是唯一二級索引,沒法確定一個單點區域有多少條索引記錄。只能通過區間最左記錄和區間最右記錄之間的頁面估算記錄數量,這種通過直接訪問索引對應的B+樹來計算某個掃描區間內對應的索引記錄條數的方式稱為index dive。這也是為什麼說在查詢真正執行前,可能少量訪問B+樹中的數據。

但是如果IN語句中的參數非常多,這種index dive帶來的性能消耗非常大。這時候可以使用索引統計數據(idnex statistics)來進行估算。MySQL提供系統變數eq_range_index_dive_limit,如果IN語句中的參數數量小於eq_range_index_dive_limit值,則使用index dive,若不小於eq_range_index_dive_limit,則使用索引統計數據。

SHOW VARIABLES LIKE 'eq_range_index_dive_limit'

 

image-20231024175024258

MySQL除了為每個表維護一份統計數據,還會為 表中的每一個索引維護一份統計數據。

SHOW INDEX FROM single_table;

 

image-20231024175827054

屬性名描述
Table 索引所屬表名
Non_unique 是否唯一。0:是。1:否
Key_name 索引名稱,其中聚簇索引的Key_name為PRIMARY
Seq_in_index 該列在索引包含的列中的位置,從1開始計數。
Column_name 該列的名稱
Cardinality 該列不重覆值的數量。聯合索引表示從索引列的第一個列開始,到當前列為止的組合不重覆的數量。
Sub_part 表示該列為字元串或位元組串類型的列建立索引的首碼字元或位元組數,若對完整的列建立索引,則為NULL。
Packed 該列的壓縮方式,NULL表示未壓縮。
Null 是否允許存儲NULL值
Index_type 該列所屬的索引的類型,一般最常見的是BTREE。

在InnoDB存儲引擎中,索引統計數據中的Cardinality是一個估值,並不精確。表統計數據的Rows也是。

根據索引統計數據中的Cardinality和表統計數據的Rows,可以計算出列中一個值平均重覆多少次:

一個值平均重覆次數 = Rows ➗ Cardinality

就可以估算出所有的單點掃描區間有多少記錄了:

記錄數量 = 一個值平均重覆次數 ✖️ 單點區間數量

這種方法相對來說比index dive簡單多了,但是不精確,適用於單點區間非常多的情況。

 

連接查詢的成本

 

什麼是條件過濾

查詢驅動表後得到的記錄條數稱為驅動表的扇出,連接查詢的成本分為兩個部分:

  • 單詞查詢驅動表的成本

  • 多次查詢被驅動表的成本(次數與驅動表的扇出決定)

顯然, 驅動表的扇出越少,連查查詢的成本越低。驅動表的扇出越多,成本成倍增加。

  • 當驅動表使用全表查詢時,扇出值就是驅動表中的記錄數量,查詢優化器根據表統計數據得到表的記錄數量,並作為扇出值。

  • 當驅動表使用二級索引查詢的計算方法,可以得出掃描區間內的記錄數量,這個就是扇出值。

  • 當驅動表無法使用索引時,只能姑且認為扇出值是全表記錄數量。

  • 當驅動表可以使用二級索引生成合適的掃描區間,但是搜索條件中包含不可以使用索引的列,那就需要在索引列形成的掃描區間的記錄數量中,猜測有多少記錄符合條件。

這個猜測過程稱為條件過濾(Condition Filtering)。猜測過程可能會使用到索引,也可能會使用到統計數據。

兩表連接成本分析

連接查詢總成本 = 單詞訪問驅動表的成本 + 驅動表扇出值 ✖️ 單詞訪問被驅動表的成本

外連接的驅動表是固定的,只需要分別為驅動表和被驅動表選擇成本最低的訪問方法,就可以得到最好的方案。

內連接驅動表和被驅動可以互換,優化器需要分別考慮兩種連接順序的成本,然後選取成本更低的連接順序,以及各個表最優訪問方法作為最終的執行計劃。

在連接查詢中,最影響成本的是驅動表扇出值✖️單詞訪問被驅動表的成本,如果這兩個值較高,成本則是成倍增長。優化重點就是:

  • 儘量減少驅動表的扇出;

  • 訪問被驅動表的成本儘量低,儘量在被驅動表的連接列上建立索引,這樣可以使用ref訪問方法。如果是主鍵或唯一二級索引那就最好了。

多表連接成本分析

分析多表連接的成本,首先要考慮多表不同的連接順序。MySQL在計算各種連接順序的成本之前,會維護一個全局變數,就是當前最小的連接查詢成本。在分析不同連接順序的成本時,將保存當前已經分析連接順序的成本最小值,如果在分析某個連接順序的成本時,該成本已經超過當前最小的連接查詢成本,那就停止分析該連接順序了,如果分析得出該連接順序的成本小於當前最小的連接查詢成本,則將覆蓋新的值。

MySQL還提供optimizer_search_depth系統變數,表示依次分析不同連接順序成本的最大表的數量。如果連接表的個數小於該值,就會分析每一種連接順序的成本;若大於,也只分析該值數量的表。該值越大,成本分析越精確,越容易生成好的執行計劃,但是消耗的時間越長;否則得到的就不是很好的執行計劃,但是節省了分析時間。

SHOW VARIABLES LIKE 'optimizer_search_depth';

 

image-20231025151714710

啟髮式規則簡介

根據以往經驗指定的一些規則,凡事不滿足這些規則的連接順序壓根就不分析。好處是減少分析不同連接順序的數量,從而減少時間;不好是不能得到最好的執行計劃。系統變數optimizer_prune_level控制是否使用啟髮式規則。

SHOW VARIABLES LIKE 'optimizer_prune_level';

 

image-20231025152823948

成本常數

MySQL中一條語句在server層進行操作的成本對應的成本常數存儲在mysql.server_cost表中,存儲引起的操作對應的成本常數存儲在mysql.engine_cost表中。

SELECT * FROM mysql.server_cost;

 

image-20231025153118255

SELECT * FROM mysql.engine_cost;

 

image-20231025153142032

閱讀自《MySQL是怎樣運行的》小孩子4919.


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • C#棧和隊列的實現 用雙向鏈表實現一個隊列 public class DoubleNode { public int Value; public DoubleNode pre; public DoubleNode next; public DoubleNode(int value) { this.V ...
  • SSL和TLS 瞭解openssl之前,我們首先要瞭解SSL\TLS是什麼。 SSL(Secure Sockets Layer)是一個協議,叫作安全套接層協議。旨在為應用層數據提供加密的傳輸層通道。,即數據從應用層去往傳輸層,會首先被ssl給加密。 但是隨著時間的發展,SSL的繼任者TLS(Tran ...
  • 每次重裝系統後,都要重新安裝軟體,配置環境變數,極為繁瑣。故作環境環境變數備份,常用軟體恢復記錄,前提是你的軟體要安裝在非系統盤,D/E盤等 軟體安裝在非系統盤 開發軟體安裝在非系統盤,建好目錄。重裝系統後,只是重置系統盤,所以這些軟體不需要重新安裝。 譬如 java 相關目錄: PS D:\jav ...
  • 內置參考電壓的使用 應用筆記 前言 CH32V/F 系列單片機能夠在一定的電壓範圍內進行工作,以 CH32V203C8T6 晶元為例,在不使用 USB 外設時,最低工作電壓能夠達到 2.4V。較為寬泛的工作電壓,允許單片機直接使用電池供電,但由於 CH32V203C8T6 晶元沒有獨立的 Vref ...
  • 1、前言 《中庸》有:“九層之台,起於壘土” 之說,那麼對於我們搞技術的人,同樣如此! 對於Linux記憶體管理,你可以說沒有留意過,但是它存在於我們日常開發的方方面面,你所打開的文件,你所創建的變數,你所運行的程式,無不以此為基礎,它可以說是操作系統的基石;只是底層被封裝的太好了,以至於我們在做開發 ...
  • 1. 線程 1.1. MySQL服務端是多線程軟體。它的每個組件都使用線程 1.2. 每個線程至少有兩個唯一標識符 1.2.1. 操作系統線程ID 1.2.2. MySQL內部線程ID 2. 對象類型 2.1. OBJECT_TYPE列 2.2. EVENT 2.3. FUNCTION 2.4. P ...
  • redis面試提問 轉載:https://blog.csdn.net/uuqaz/article/details/127088333 Redis 是面試中繞不過的檻,只要在簡歷中寫了用過 Redis,肯定逃不過。今天我們就來模擬一下麵試官在 Redis 這個話題上是如何一步一步深入,全面考察候選人對 ...
  • YARN(Yet Another Resource Negotiator)是 Hadoop 生態系統中的資源調度器,主要用於資源管理和作業調度。YARN 自身具備隊列管理功能,通過對 YARN 資源隊列進行配置和管理,實現集群資源的分配,以滿足不同應用和用戶的需求。YARN 的引入為集群在利用率、資 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...