(譯)MySQL中的直方圖統計信息

来源:https://www.cnblogs.com/wy123/archive/2022/12/05/16944785.html
-Advertisement-
Play Games

什麼是直方圖,在MySQL 8.0.3如何創建直方圖?MySQL的直方圖是如何影響執行計劃生成的?創建直方圖有哪些註意事項?直方圖和索引對優化器的選擇上有什麼差異,又該如何選擇?如何判斷直方圖對執行計劃的影響?MySQL官方blog的這篇文章用非常具體的示例回答了這一系列問題,let's go。原文 ...


什麼是直方圖,在MySQL 8.0.3如何創建直方圖?
MySQL的直方圖是如何影響執行計劃生成的?
創建直方圖有哪些註意事項?
直方圖和索引對優化器的選擇上有什麼差異,又該如何選擇?
如何判斷直方圖對執行計劃的影響?
MySQL官方blog的這篇文章用非常具體的示例回答了這一系列問題,let's go。
原文地址為https://dev.mysql.com/blog-archive/histogram-statistics-in-mysql/,以下為譯文:     從MySQL 8.0.3開始,您可以創建直方圖統計信息,以便向優化器提供更多的統計信息。在這篇博文中,我們將看看如何創建直方圖統計數據,並解釋何時使用直方圖統計數據可能有用。

什麼是直方圖

查詢優化器是資料庫中負責將SQL查詢轉換為儘可能高效的執行計劃的部分。有時,查詢優化器無法找到最有效的計劃,並最終花費比所實際所需更多的時間來執行查詢。出現這種情況的主要原因通常是優化器對它要查詢的數據分佈沒有足夠的瞭解:
  • 每個表中有多少行?
  • 每一列有多少不同的值?
  • 數據如何分佈在每一列中?
一個查詢優化器缺少重要信息的示例,人們每天睡覺時間的數據表:
CREATE TABLE bedtime (
  person_id INT,
  time_of_day TIME);
對於“time_of_day”這個欄位,大多數值很可能是在11:00PM左右,因為大多數人是在這個時間段睡覺的。所以下麵第一個查詢返回的數據行數要比第二個查詢返回的數據要多。
1) SELECT * FROM bedtime WHERE time_of_day BETWEEN "22:00:00" AND "23:59:00"
2) SELECT * FROM bedtime WHERE time_of_day BETWEEN "12:00:00" AND "14:00:00"
在沒有任何統計數據可用的情況下,優化器將假設“time_of_day”中的值是均勻分佈的(即,一個人在下午3點左右睡覺的可能性與晚上11點左右睡覺的可能性相同)。如何使查詢優化器意識到數據中的這種偏斜度?對此的一個解決方案是為該列創建直方圖統計信息。
直方圖是一列數據分佈的近似值。它可以相當準確地告訴您,您的數據是否有偏差,這反過來將幫助資料庫伺服器理解它所包含的數據的性質。直方圖有很多不同的風格,在MySQL中我們選擇支持兩種不同的類型:“單例(等寬)”直方圖和“等高”直方圖。所有直方圖類型的共同點是,它們將數據集分割為一組“桶”,MySQL自動將值劃分為桶,並自動決定創建什麼類型的直方圖。

如何創建和刪除直方圖統計

為了管理直方圖統計數據,我們擴展了ANALYZE TABLE,增加了兩個新的子句:

ANALYZE TABLE tbl_name UPDATE HISTOGRAM ON col_name [, col_name] WITH N BUCKETS;
ANALYZE TABLE tbl_name DROP HISTOGRAM ON col_name [, col_name];
第一種語法允許你同時為一個或多個列創建直方圖統計數據:
mysql> ANALYZE TABLE payment UPDATE HISTOGRAM ON amount WITH 32 BUCKETS;
+----------------+-----------+----------+---------------------------------------------------+
| Table          | Op        | Msg_type | Msg_text                                          |
+----------------+-----------+----------+---------------------------------------------------+
| sakila.payment | histogram | status   | Histogram statistics created for column 'amount'. |
+----------------+-----------+----------+---------------------------------------------------+
1 row in set (0.27 sec)
 
mysql> ANALYZE TABLE payment UPDATE HISTOGRAM ON amount, payment_date WITH 32 BUCKETS;
+----------------+-----------+----------+---------------------------------------------------------+
| Table          | Op        | Msg_type | Msg_text                                                |
+----------------+-----------+----------+---------------------------------------------------------+
| sakila.payment | histogram | status   | Histogram statistics created for column 'amount'.       |
| sakila.payment | histogram | status   | Histogram statistics created for column 'payment_date'. |
+----------------+-----------+----------+---------------------------------------------------------+
請註意,必須指定桶的數量,並且可以在 1 到 1024 的範圍內(預設為100)。您應該為數據集選擇多少個桶取決於幾個因素;您有多少個不同的值,您的數據集有多大偏差,您需要多高的準確性等。
但是,在一定數量的桶之後,(再繼續加大桶的數據量)對準確性的提高效果相當低。所以我們建議從較低的數字開始,例如 32,如果您發現它不符合您的需求,則增加它。   在上面的例子中,我們可以看到我們已經為列“amount”構建了兩次直方圖。在第一個查詢中,創建了一個新的直方圖。在第二個查詢中,“amount”的直方圖會自動覆蓋。
如果你想刪除你創建的任何直方圖統計數據,你只需使用DROP histogram語法:
mysql> ANALYZE TABLE payment DROP HISTOGRAM ON payment_date;
+----------------+-----------+----------+---------------------------------------------------------+
| Table          | Op        | Msg_type | Msg_text                                                |
+----------------+-----------+----------+---------------------------------------------------------+
| sakila.payment | histogram | status   | Histogram statistics removed for column 'payment_date'. |
+----------------+-----------+----------+---------------------------------------------------------+
與UPDATE HISTOGRAM一樣,您可以在同一個命令中指定多個列。值得註意的一個特性是,ANALYZE TABLE命令將嘗試執行儘可能多的工作,即使在命令執行過程中出現了錯誤。假設您指定了三列,但是第二列不存在。伺服器仍然會為第一和第三列創建和存儲直方圖:
mysql> ANALYZE TABLE customer UPDATE HISTOGRAM ON c_birth_day, c_foobar, c_birth_month WITH 32 BUCKETS;
+----------------+-----------+----------+----------------------------------------------------------+
| Table          | Op        | Msg_type | Msg_text                                                 |
+----------------+-----------+----------+----------------------------------------------------------+
| tpcds.customer | histogram | status   | Histogram statistics created for column 'c_birth_day'.   |
| tpcds.customer | histogram | status   | Histogram statistics created for column 'c_birth_month'. |
| tpcds.customer | histogram | Error    | The column 'c_foobar' does not exist.                    |
+----------------+-----------+----------+----------------------------------------------------------+
3 rows in set (0.15 sec)

直方圖的創建在資料庫內部是如何實現的?

如果您已經閱讀了MySQL手冊,您可能已經看到了新的系統變數histogram_generation_max_mem_size這個變數將控制伺服器在生成直方圖統計數據時允許使用的記憶體大小(以位元組計)。那你為什麼要控制它呢?

當您指定想要構建一個直方圖時,伺服器將把所有數據讀入記憶體併在記憶體中執行所有工作(包括排序)。如果您想在一個非常大的表上生成一個直方圖,那麼您可能要冒著將數百兆位元組的數據讀入記憶體的風險,這可能是不可取的。因此,為了處理這個問題,MySQL將計算在給定由系統變數histogram_generation_max_mem_size指定的記憶體量的情況下,它可以將多少行數據放入記憶體中。如果它意識到它只能在給定的記憶體限制內裝入行的一個子集,它將求助於抽樣。這可以通過查看屬性“採樣率”來觀察:

mysql> SET histogram_generation_max_mem_size = 1000000;
Query OK, 0 rows affected (0.00 sec)
 
mysql> ANALYZE TABLE customer UPDATE HISTOGRAM ON c_birth_country WITH 16 BUCKETS;
+----------------+-----------+----------+------------------------------------------------------------+
| Table          | Op        | Msg_type | Msg_text                                                   |
+----------------+-----------+----------+------------------------------------------------------------+
| tpcds.customer | histogram | status   | Histogram statistics created for column 'c_birth_country'. |
+----------------+-----------+----------+------------------------------------------------------------+
1 row in set (0.22 sec)
 
mysql> SELECT histogram->>'$."sampling-rate"'
    -> FROM   information_schema.column_statistics
    -> WHERE  table_name = "customer"
    ->        AND column_name = "c_birth_country";
+---------------------------------+
| histogram->>'$."sampling-rate"' |
+---------------------------------+
| 0.048743243211626014            |
+---------------------------------+
在這裡,我們可以看到優化器通過讀取“c_birth_country”列中大約4.8%的數據創建了一個直方圖。值得註意的是,抽樣是不確定的,因此如果使用抽樣,在同一個數據集上的兩次後續調用“ANALYZE TABLE tbl UPDATE HISTOGRAM…”可能會給您兩個不同的直方圖。

Query examples

那麼,使用直方圖統計可以得到什麼呢?讓我們看看TPC-DS Benchmark Suite中的幾個查詢,其中添加一個直方圖可以在查詢執行時間上產生很大的差異。下麵我們將使用規模繫數為1的TPC-DS,這意味著資料庫的大小大約為1GB。這台機器是英特爾酷睿i7-4770,運行Debian Stretch和MySQL 8.0 RC1。這個配置是相當標準的,除了innodb_buffer_pool_size被增加到2G,以便我們可以將整個資料庫放入緩衝池中。

為了讓優化器實際使用直方圖提供的統計數據,您只需確保優化器開關“condition_fanout_filter”處於打開狀態。註意,這在預設情況下是打開的。

Query 90

Benchmark Suite 將此查詢描述為“具有特定家屬人數的客戶早上通過互聯網售出的商品數量與晚上售出的商品數量之間的比率是多少。僅考慮具有大量內容的網站。
mysql> SELECT CAST(amc AS DECIMAL(15, 4)) / CAST(pmc AS DECIMAL(15, 4)) am_pm_ratio
    -> FROM   (SELECT COUNT(*) amc
    ->         FROM   web_sales,
    ->                household_demographics,
    ->                time_dim,
    ->                web_page
    ->         WHERE  ws_sold_time_sk = time_dim.t_time_sk
    ->                AND ws_ship_hdemo_sk = household_demographics.hd_demo_sk
    ->                AND ws_web_page_sk = web_page.wp_web_page_sk
    ->                AND time_dim.t_hour BETWEEN 9 AND 9 + 1
    ->                AND household_demographics.hd_dep_count = 2
    ->                AND web_page.wp_char_count BETWEEN 5000 AND 5200) at,
    ->        (SELECT COUNT(*) pmc
    ->         FROM   web_sales,
    ->                household_demographics,
    ->                time_dim,
    ->                web_page
    ->         WHERE  ws_sold_time_sk = time_dim.t_time_sk
    ->                AND ws_ship_hdemo_sk = household_demographics.hd_demo_sk
    ->                AND ws_web_page_sk = web_page.wp_web_page_sk
    ->                AND time_dim.t_hour BETWEEN 15 AND 15 + 1
    ->                AND household_demographics.hd_dep_count = 2
    ->                AND web_page.wp_char_count BETWEEN 5000 AND 5200) pt
    -> ORDER  BY am_pm_ratio
    -> LIMIT  100;
+-------------+
| am_pm_ratio |
+-------------+
|  1.27619048 |
+-------------+
1 row in set (1.48 sec)
View Code 正如我們所見,執行查詢大約需要 1.5 秒。這看起來並不多,但是通過在單個列上添加直方圖,我們可以使該查詢的運行速度提高三倍(為了便於閱讀,查詢被截斷了);
mysql> ANALYZE TABLE web_page UPDATE HISTOGRAM ON wp_char_count WITH 8 BUCKETS;
+----------------+-----------+----------+----------------------------------------------------------+
| Table          | Op        | Msg_type | Msg_text                                                 |
+----------------+-----------+----------+----------------------------------------------------------+
| tpcds.web_page | histogram | status   | Histogram statistics created for column 'wp_char_count'. |
+----------------+-----------+----------+----------------------------------------------------------+
1 row in set (0.06 sec)
 
mysql> SELECT ...
+-------------+
| am_pm_ratio |
+-------------+
|  1.27619048 |
+-------------+
1 row in set (0.50 sec)
View Code 對於這個直方圖,查詢現在大約需要0.5秒。為什麼呢? 主要原因可以通過謂詞“web_page.wp_char_count BETWEEN 5000 AND 5200 "得到。在沒有任何統計數據可用的情況下,優化器假定表“web_page”中有11.11%的行匹配給定的謂詞。然而,這是錯誤的。通過驗證表裡的數據,我們可以看到只有1.6%匹配這個謂詞(60行中有一行):
mysql> SELECT
    ->   (SELECT COUNT(*) FROM web_page WHERE web_page.wp_char_count BETWEEN 5000 AND 5200)
    ->   /
    ->   (SELECT COUNT(*) FROM web_page) AS ratio;
+--------+
| ratio  |
+--------+
| 0.0167 |
+--------+
1 row in set (0.00 sec)
有了直方圖統計信息,優化器現在知道了這一點,併在連接順序中提前推入表(譯註:原文是pushes the table earlier in the join order,應該是將相關的表選為驅動表,符合小表驅動大表的原則),從而生成執行計劃,執行速度提高三倍。

Query 61

該查詢描述為“查找給定月份和年份中有促銷和沒有促銷的商品的銷售比例”。只有出售給生活在特定時區的客戶的特定類別的產品才會被考慮。”這是一個包含多個連接的複雜大查詢:
mysql> SELECT promotions,                                                                                                                                                                                                ->        total,
    ->        CAST(promotions AS DECIMAL(15, 4)) / CAST(total AS DECIMAL(15, 4)) * 100
    -> FROM   (SELECT SUM(ss_ext_sales_price) promotions
    ->         FROM   store_sales,
    ->                store,
    ->                promotion,
    ->                date_dim,
    ->                customer,
    ->                customer_address,
    ->                item
    ->         WHERE  ss_sold_date_sk = d_date_sk
    ->                AND ss_store_sk = s_store_sk
    ->                AND ss_promo_sk = p_promo_sk
    ->                AND ss_customer_sk = c_customer_sk
    ->                AND ca_address_sk = c_current_addr_sk
    ->                AND ss_item_sk = i_item_sk
    ->                AND ca_gmt_offset = -5
    ->                AND i_category = 'Home'
    ->                AND ( p_channel_dmail = 'Y'
    ->                       OR p_channel_email = 'Y'
    ->                       OR p_channel_tv = 'Y' )
    ->                AND s_gmt_offset = -5
    ->                AND d_year = 2000
    ->                AND d_moy = 12) promotional_sales,
    ->        (SELECT SUM(ss_ext_sales_price) total
    ->         FROM   store_sales,
    ->                store,
    ->                date_dim,
    ->                customer,
    ->                customer_address,
    ->                item
    ->         WHERE  ss_sold_date_sk = d_date_sk
    ->                AND ss_store_sk = s_store_sk
    ->                AND ss_customer_sk = c_customer_sk
    ->                AND ca_address_sk = c_current_addr_sk
    ->                AND ss_item_sk = i_item_sk
    ->                AND ca_gmt_offset = -5
    ->                AND i_category = 'Home'
    ->                AND s_gmt_offset = -5
    ->                AND d_year = 2000
    ->                AND d_moy = 12) all_sales
    -> ORDER  BY promotions,
    ->           total
    -> LIMIT  100;
+------------+------------+--------------------------------------------------------------------------+
| promotions | total      | CAST(promotions AS DECIMAL(15, 4)) / CAST(total AS DECIMAL(15, 4)) * 100 |
+------------+------------+--------------------------------------------------------------------------+
| 3213210.07 | 5966836.78 |                                                              53.85114741 |
+------------+------------+--------------------------------------------------------------------------+
1 row in set (2.78 sec)
View Code 從輸出中可以看到,執行查詢大約需要2.8秒。然而,查詢優化器沒有意識到列“s_gmt_offset”中只有一個不同的值。在沒有任何統計數據可用的情況下,優化器使用一些硬編碼的估計,這假設10%的行將匹配謂詞“ca_gmt_offset = -5”如果我們為這一列添加一個直方圖,優化器現在知道表中的所有行都將滿足條件,從而為我們提供一個更好的執行計劃(為了更好的可讀性,查詢被截斷):
mysql> ANALYZE TABLE store UPDATE HISTOGRAM ON s_gmt_offset WITH 8 BUCKETS;
+-------------+-----------+----------+---------------------------------------------------------+
| Table       | Op        | Msg_type | Msg_text                                                |
+-------------+-----------+----------+---------------------------------------------------------+
| tpcds.store | histogram | status   | Histogram statistics created for column 's_gmt_offset'. |
+-------------+-----------+----------+---------------------------------------------------------+
1 row in set (0.06 sec)
 
mysql> SELECT ...
+------------+------------+--------------------------------------------------------------------------+
| promotions | total      | CAST(promotions AS DECIMAL(15, 4)) / CAST(total AS DECIMAL(15, 4)) * 100 |
+------------+------------+--------------------------------------------------------------------------+
| 3213210.07 | 5966836.78 |                                                              53.85114741 |
+------------+------------+--------------------------------------------------------------------------+
1 row in set (1.37 sec)
V
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JZ31 棧的壓入、彈出序列 描述 輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否可能為該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1,2,3,4,5是某棧的壓入順序,序列4,5,3,2,1是該壓棧序列對應的一個彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序 ...
  • Hello,大家好,又是好久不見,最近太忙了(藉口)。看了下日誌,有 2 個月沒寫文章了。為了證明公眾號還活著,今天必須更新一下了。 在我們的開發過程中,總有那麼些需求是那麼的變態。常規的方案已經無法滿足。比如某些規則非常複雜,而客戶又經常要修改它。那麼我們可能需要把這部分代碼直接做為配置文件提取出 ...
  • 從技術生態發展過程及理念、產品級解決方案理念、產品系統框架及主要功能介紹、產品系統二次開發和應用案例等5個方面進行了主題發言。 ...
  • 一:背景 1.講故事 這周有個朋友找到我,說他的程式出現了記憶體緩慢增長,沒有回頭的趨勢,讓我幫忙看下到底怎麼回事,據朋友說這個問題已經困擾他快一周了,還是沒能找到最終的問題,看樣子這個問題比較刁鑽,不管怎麼說,先祭出 WinDbg。 二:WinDbg 分析 1. 托管還是非托管泄露 一直關註這個系列 ...
  • 前言 本文藉鑒文章:https://www.yuque.com/dengfenglai-esbap/kb/mc4k41?#xOxNG 在此基礎上修改了一點(照著原來的做沒成功),感謝這位師傅給的資源。 1、環境準備 1、主機:伺服器CentOs7 2、Docker版本:20.10.2 3、Docke ...
  • 一、引子 我們都知道對指針( Pointer)的操作,實際上是對電腦記憶體地址的操作,通過訪問記憶體地址實現間接訪問該地址中保存的數據。其實就是CPU的定址方式中的間接定址。簡單概括正常使用指針時的3個步驟為: 定義指針變數 綁定指針即給指針變數賦值 解引用即間接訪問目標變數通過一個簡單的例子來看這3 ...
  • 11章 併發數據 瞭解併發需要先瞭解的概念: 1.資料庫是一個可以共用資源,可以多用戶同時使用一個資料庫,該資料庫稱為多用戶資料庫,如訂票系統、銀行系統 2.多事務執行方式: 1.事務串列執行 : 務執行完在執行下一個事務 - 缺點資源浪費 2.交叉併發方式 : 在單處理機系統中,事務的並行事務是提 ...
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 作者:王權富貴 1.概述 MySQL的分區表沒有禁止NULL值作為分區表達式的值,無論它是列值還是用戶提供的表達式的值,需要記住NULL值不是數字。My ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...