[翻譯]——How the MySQL Optimizer Calculates the Cost of a Query (Doc ID 1327497.1)

来源:https://www.cnblogs.com/kerrycode/archive/2023/11/06/17813862.html
-Advertisement-
Play Games

本文是對這篇文章How the MySQL Optimizer Calculates the Cost of a Query (Doc ID 1327497.1)[1]的翻譯,翻譯如有不當的地方,敬請諒解,請尊重原創和翻譯勞動成果,轉載的時候請註明出處。謝謝! 適用於: MySQL 4.0 及後續更 ...


本文是對這篇文章How the MySQL Optimizer Calculates the Cost of a Query (Doc ID 1327497.1)[1]的翻譯,翻譯如有不當的地方,敬請諒解,請尊重原創和翻譯勞動成果,轉載的時候請註明出處。謝謝!

適用於:

MySQL 4.0 及後續更高的版本

本文檔中的內容適用於任何平臺。

目標

瞭解 MySQL 優化器如何計算SQL語句的查詢成本/代價以及如何分析EXPLAIN 語句的輸出。

解決方案

MySQL優化器使用成本模型(cost model),其中查詢計劃的總體成本由各種操作(operation)的成本總體決定。確定成本的主要方法是使用存儲提供的統計數據並使用所謂的成本常量(另請參閱下麵的“對資料庫成本模型進行更改”部分)。例如,這些統計數據是索引基數值(即索引中值的唯一性的度量)和每個表中的總行數。由於統計數據的不精確性,因為統計數據可能已經過時,或者它是使用近似方法來計算獲取的統計數據(對於InnoDB資料庫尤其如此,見下文),並且值的分佈未知,那麼優化器只能執行查詢時提供查詢結果集的行數的估計值。該預估值在某些情況下非常準確,但在某些情況下則不太準確。 實際的實現方式比上面的描述更複雜,確切的細節也可能取決於查詢的類型。另請參閱參考手冊中的優化器成本模型,瞭解 MySQL 5.7 及更高版本中優化器成本模型的討論。 在 MySQL 5.6 及後續更高版本中,當我們在評估優化器如何執行查詢語句時,優化器跟蹤功能(optimizer trace feature)可用於深入瞭解優化器的決策過程。更多詳細信息,請參閱註釋 2241524.1[2]

更改資料庫的成本模型

在 MySQL 5.7 以及後續版本中,優化器模型中使用的成本常量(cost constants)由資料庫用來進行成本估算。如果需要的話,可以更改此資料庫的配置。

警告:更改資料庫的成本模型被認為是高級資料庫管理。如果您進行更改以驗證其行為是否符合預期時,請務必小心,併在部署到生產環境之前進行完全徹底充分的測試。

從 MySQL 5.7.17 開始,資料庫成本模型中有兩個可以調整的變數。數據存儲在mysql.engine_cost表中,預設值為:

mysql> SELECT * FROM mysql.engine_cost;
+-------------+-------------+------------------------+------------+---------------------+---------+
| engine_name | device_type | cost_name              | cost_value | last_update         | comment |
+-------------+-------------+------------------------+------------+---------------------+---------+
default     |           0 | io_block_read_cost     |       NULL | 2017-01-13 15:21:46 | NULL    |
default     |           0 | memory_block_read_cost |       NULL | 2017-01-13 15:21:46 | NULL    |
+-------------+-------------+------------------------+------------+---------------------+---------+
2 rows in set (0.00 sec)

io_block_read_cost和memory_block_read_cost參數分別指定從磁碟和記憶體讀取數據的相對成本。將 io_block_read_cost 參數的cost_value值設置為高於 memory_block_read_cost 的值,這會使優化器生成的查詢計劃更喜歡讀取記憶體中數據,而不是從磁碟讀取數據。 還可以通過將engine_name設置為要生效的存儲引擎的名稱來指定每個存儲引擎的成本值。 當所有變更生效後,使用 FLUSH OPTIMIZER_COSTS 命令觸發優化器重新讀取成本模型數據。 每個會話的優化器成本都會被緩存。只有在 FLUSH OPTIMIZER_COSTS 語句執行之後啟動的會話才會受到更改的影響。   例如,通過使用以下步驟將io_block_read_cost預設值設置為 2.0,將 InnoDB 存儲引擎的預設值設置為 3.0:

1.將io_block_read_cost的預設值更新為 2.0:

mysql> UPDATE mysql.engine_cost
          SET cost_value = 2.0
        WHERE cost_name = 'io_block_read_cost';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

2.為InnoDB存儲引擎添加新的成本規則,將io_block_read_cost設置為3.0:

mysql> INSERT INTO mysql.engine_cost (engine_name, device_type, cost_name, cost_value, comment)
       VALUES ('InnoDB'0'io_block_read_cost'3.0'Using a slower disk for InnoDB');
Query OK, 1 row affected (0.01 sec)

3.刷新新的成本值:

mysql> FLUSH OPTIMIZER_COSTS;
Query OK, 0 rows affected (0.00 sec)

4.驗證新的成本值是否在查詢計劃中給出了預期結果。

5.根據需要部署到生產。

InnoDB

如果您的表使用InnoDB 存儲引擎,您還應該註意索引統計信息是基於有限數量記錄的隨機樣本/採樣的估計。如果您運行ANALYZE TABLE tablename(將tablename替換為實際的表名),然後運行SHOW INDEXES FROM tablename,那麼您將看到基數將在後續一系列操作之間波動。表中的總行數也是如此。如果多次執行SHOW TABLE STATUS LIKE 'tablename',即使表上沒有發生任何更新,那麼您將看到每次執行時行總數的估計值也會有所不同(另一方面,對於MyISAM存儲引擎表,表中的行數是精確的,因為其缺乏多版本控制和使用表鎖,使得維護精確的統計數據變得更容易)。然而,您也可能不走運,最終對要檢查的行數的估計相對較差。這也意味著,如果您運行ANALYZE TABLE tablename,那麼行列中的值可能會發生變化,在某些情況下甚至查詢計劃本身也會發生變化。另請參閱MySQL 參考手冊中的InnoDB 表限制。

註意:如果innodb_stats_on_metadata設置為ON(MySQL 5.5 及更早版本中的預設值),InnoDB會在元數據語句(例如SHOW TABLE STATUS或SHOW INDEX )執行期間訪問INFORMATION_SCHEMA表TABLES或STATISTICS時更新統計信息。

為了更好地估計 InnoDB 表的索引基數,可以將參數innodb_stats_sample_pages設置為更大的值。但需要註意,較大的值將導致索引更新時間更長,並且每次打開表時都會重新計算統計信息,因此可能會對性能產生影響。另請參閱MySQL 優化器團隊的Oystein Grovlen 的博客[3],文中討論了更改innodb_stats_sample_pages的值如何影響索引基數計算。 innodb_stats_sample_pages選項是隨 MySQL 5.1.38 中的 InnoDB 插件一起引入的。在MySQL 5.1和MySQL 5.0及更早版本的內置InnoDB中,該值無法更改,並且預設值為8。在 MySQL 5.6.3 中,innodb_stats_sample_pages已替換為innodb_stats_transient_sample_pages選項。對於 MySQL 5.6.2 及更高版本,另請參閱innodb_stats_persistent_sample_pages。   實際檢查行數示例 要更好地估計實際檢查的行數,請在查詢之前和查詢完成之後執行SHOW SESSION STATUS LIKE 'handler%'命令。另一種可能性是確保查詢被慢查詢日誌記錄,慢查詢日誌還提供檢查的總行數。

使用會話狀態變數查找檢查的行數的示例是:

mysql> SHOW SESSION STATUS LIKE 'handler%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Handler_commit             | 0     |
| Handler_delete             | 0     |
| Handler_discover           | 0     |
| Handler_prepare            | 0     |
| Handler_read_first         | 0     |
| Handler_read_key           | 0     |
| Handler_read_last          | 0     |
| Handler_read_next          | 0     |
| Handler_read_prev          | 0     |
| Handler_read_rnd           | 0     |
| Handler_read_rnd_next      | 0     |
| Handler_rollback           | 0     |
| Handler_savepoint          | 0     |
| Handler_savepoint_rollback | 0     |
| Handler_update             | 0     |
| Handler_write              | 0     |
+----------------------------+-------+
16 rows in set (0.00 sec)

mysql> SELECT * FROM (SELECT id FROM t1 WHERE id < 100) t1 INNER JOIN t2 USING (id);
+----+-----+
| id | val |
+----+-----+
|  1 | a   |
|  2 | b   |
|  3 | c   |
...
| 98 | c   |
| 99 | a   |
+----+-----+
76 rows in set (0.00 sec)

mysql> SHOW SESSION STATUS LIKE 'handler%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Handler_commit             | 1     |
| Handler_delete             | 0     |
| Handler_discover           | 0     |
| Handler_prepare            | 0     |
| Handler_read_first         | 1     |
| Handler_read_key           | 100   |
| Handler_read_last          | 0     |
| Handler_read_next          | 99    |
| Handler_read_prev          | 0     |
| Handler_read_rnd           | 0     |
| Handler_read_rnd_next      | 100   |
| Handler_rollback           | 0     |
| Handler_savepoint          | 0     |
| Handler_savepoint_rollback | 0     |
| Handler_update             | 0     |
| Handler_write              | 99    |
+----------------------------+-------+
16 rows in set (0.00 sec)

這表明SQL查詢總共讀取了 300 行,分佈如下:

  • 1 行讀取索引中的第一個條目 (Handler_read_first)
  • 99 行按鍵順序讀取下一行(Handler_read_next)
  • 100 行從索引查找行(Handler_read_key)
  • 100 行進行表掃描 (Handler_read_rnd_next) 此外,由於派生表 (Handler_write),有 99 行被寫入內部臨時表。

參考資料

[1]

原文: https://support.oracle.com/epmos/faces/DocumentDisplay?id=1327497.1

[2]

1: https://support.oracle.com/epmos/faces/DocumentDisplay?parent=DOCUMENT&sourceId=1327497.1&id=2241524.1

[3]

Oystein Grovlen: http://oysteing.blogspot.com/2011/04/more-stable-query-execution-time-by.html

掃描上面二維碼關註我 如果你真心覺得文章寫得不錯,而且對你有所幫助,那就不妨幫忙“推薦"一下,您的“推薦”和”打賞“將是我最大的寫作動力! 本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接.
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 線程池(重點) 線程池:三大方法、七大參數、四種拒絕策略 池化技術 程式的運行,本質:占用系統的資源!優化資源的使用!-> 池化技術(線程池、連接池、對象池......);創建和銷毀十分消耗資源 池化技術:事先準備好一些資源,有人要用就拿,拿完用完還給我。 線程池的好處: 1、降低資源消耗 2、提高 ...
  • 我們在類中通常會有一個屬性為 IsDel來表示軟刪除或也稱邏輯刪除,這個屬性會導致我們在進行查詢操作時,每一次都要 .where(s=>s.IsDel==false) 非常的麻煩。在使用efCore時可以通過配置查詢篩選器來很好的解決這個問題。 public class SysUser { publ ...
  • 搜索查找指令 find 指令 find指令將從指定目錄向下遞歸的遍歷其各個子目錄,將滿足條件的文件或者目錄顯示在終端。 基本語法 find [搜索範圍(指定目錄)] [選項] 選項說明 選項 功能 -name<查詢方式> 按照指定的文件名查找模式查找文件 -user<用戶名> 查找屬於指定用戶名所有 ...
  • rsyslog 介紹 日誌(Log)是記錄和存儲電腦、軟體、應用或其他系統的操作和事件的文件或數據流。它們可以為系統管理員、開發人員和最終用戶提供詳細的背景信息,以幫助他們瞭解和診斷系統的行為。 rsyslog 是一個開源的日誌處理工具,一般用在類Unix系統上,是syslogd 的擴展。它提供了 ...
  • 包括線程概念簡介;線程創建函數pthread_create以及退出、回收等;線程同步互斥鎖pthread_mutex_t,讀寫鎖pthread_rwlock_t,條件變數pthread_cond_t以及信號量semaphore ...
  • 本文分享自華為雲社區《GaussDB資料庫SQL系列-數據去重》,作者: Gauss松鼠會小助手2 。 一、前言 數據去重在資料庫中是比較常見的操作。複雜的業務場景、多業務線的數據來源等等,都會帶來重覆數據的存儲。本文以GaussDB資料庫為實驗平臺,將為大家詳細講解如何去重。 二、數據去重應用場景 ...
  • 本篇文章記錄最近ES做節點替換,從shard遷移過程中被鎖定導致無法分配,主shard正常,希望可以幫助其它人 failed to create shard,failed to obtain in-memory shard lock,ShardLockObtainFailedException 一、 ...
  • 設置進程記憶體(Process Memory) Apache Flink通過嚴格控制其各種組件的記憶體使用,在JVM之上提供高效的工作負載。 配置總記憶體(Total Memory) Flink JVM進程的總進程記憶體(total process memory)由Flink應用程式消耗的記憶體(總Flink ...
一周排行
    -Advertisement-
    Play Games
  • 前言 推薦一款基於.NET 8、WPF、Prism.DryIoc、MVVM設計模式、Blazor以及MySQL資料庫構建的企業級工作流系統的WPF客戶端框架-AIStudio.Wpf.AClient 6.0。 項目介紹 框架採用了 Prism 框架來實現 MVVM 模式,不僅簡化了 MVVM 的典型 ...
  • 先看一下效果吧: 我們直接通過改造一下原版的TreeView來實現上面這個效果 我們先創建一個普通的TreeView 代碼很簡單: <TreeView> <TreeViewItem Header="人事部"/> <TreeViewItem Header="技術部"> <TreeViewItem He ...
  • 1. 生成式 AI 簡介 https://imp.i384100.net/LXYmq3 2. Python 語言 https://imp.i384100.net/5gmXXo 3. 統計和 R https://youtu.be/ANMuuq502rE?si=hw9GT6JVzMhRvBbF 4. 數 ...
  • 本文為大家介紹下.NET解壓/壓縮zip文件。雖然解壓縮不是啥核心技術,但壓縮性能以及進度處理還是需要關註下,針對使用較多的zip開源組件驗證,給大家提供個技術選型參考 之前在《.NET WebSocket高併發通信阻塞問題 - 唐宋元明清2188 - 博客園 (cnblogs.com)》講過,團隊 ...
  • 之前寫過兩篇關於Roslyn源生成器生成源代碼的用例,今天使用Roslyn的代碼修複器CodeFixProvider實現一個cs文件頭部註釋的功能, 代碼修複器會同時涉及到CodeFixProvider和DiagnosticAnalyzer, 實現FileHeaderAnalyzer 首先我們知道修 ...
  • 在軟體行業,經常會聽到一句話“文不如表,表不如圖”說明瞭圖形在軟體應用中的重要性。同樣在WPF開發中,為了程式美觀或者業務需要,經常會用到各種個樣的圖形。今天以一些簡單的小例子,簡述WPF開發中幾何圖形(Geometry)相關內容,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 在 C# 中使用 RabbitMQ 通過簡訊發送重置後的密碼到用戶的手機號上,你可以按照以下步驟進行 1.安裝 RabbitMQ 客戶端庫 首先,確保你已經安裝了 RabbitMQ 客戶端庫。你可以通過 NuGet 包管理器來安裝: dotnet add package RabbitMQ.Clien ...
  • 1.下載 Protocol Buffers 編譯器(protoc) 前往 Protocol Buffers GitHub Releases 頁面。在 "Assets" 下找到適合您系統的壓縮文件,通常為 protoc-{version}-win32.zip 或 protoc-{version}-wi ...
  • 簡介 在現代微服務架構中,服務發現(Service Discovery)是一項關鍵功能。它允許微服務動態地找到彼此,而無需依賴硬編碼的地址。以前如果你搜 .NET Service Discovery,大概率會搜到一大堆 Eureka,Consul 等的文章。現在微軟為我們帶來了一個官方的包:Micr ...
  • ZY樹洞 前言 ZY樹洞是一個基於.NET Core開發的簡單的評論系統,主要用於大家分享自己心中的感悟、經驗、心得、想法等。 好了,不賣關子了,這個項目其實是上班無聊的時候寫的,為什麼要寫這個項目呢?因為我單純的想吐槽一下工作中的不滿而已。 項目介紹 項目很簡單,主要功能就是提供一個簡單的評論系統 ...