天天看點

MySQL8.0新特性學習筆記(三):直方圖

目錄

一,直方圖簡介

二,直方圖的生成

三,直方圖的删除

四,直方圖的存儲

五,舉例說明直方圖的作用

六,直方圖和索引

七,獲得直方圖的資訊

一,直方圖簡介

直方圖,histogram,直譯就是柱狀圖,國内多翻譯為直方圖。

直方圖不是圖,是對表的某個字段資料分布的統計,這種資料統計是基于某字段的實際存儲資料分布得出的。

這種分布統計可以幫助查詢優化器掌握字段真實的情況,進而得出更高效而切合實際的執行計劃。

在有直方圖之前,優化器對某字段的資料分布預估有可能是不準确的,甚至可能和實際情況差距比較大,錯誤的預估會導緻不高效的執行計劃。

從Explain sql的角度看,建立直方圖後會影響Explain結果的filter字段,使之成為較為真實的比例,進而影響執行計劃的選擇。

直方圖分為單值直方圖(singleton histograms)和等高直方圖(equi-height histograms)。

不知道為什麼國内多把singleton histograms翻譯成等寬直方圖。

二,直方圖的生成

生成直方圖的sql語句是這樣的:

ANALYZE TABLE tbl_name UPDATE HISTOGRAM ON col_name [, col_name] WITH N BUCKETS;
           

可以看到,可以同時對多個字段生成直方圖。

BUCKETS是統計結果的分組個數,如果不寫則預設100,可選值從1到1024。數字越大統計結果越精确,官方建議從小值慢慢增加到可以滿足預期的大小。

直方圖生成的時候,MySQL會把所有相關資料讀入記憶體中,然後進行分組統計。

8.0新增的參數histogram_generation_max_mem_size定義了直方圖能用多大的記憶體。

如果字段值超過限定記憶體,則會取樣,樣本随機,也就是說兩次生成的直方圖内容可能不一樣。

官方例子:

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            |
+---------------------------------+
1 row in set (0.00 sec)
           

sampling-rate是查詢直方圖取樣比例用的,這次的查詢結果代表此字段的直方圖是大概取樣4.8%得到的。

一個字段隻能生成一個直方圖,重複生成會把前者覆寫。

生成直方圖的sql中如果有不存在的字段,MySQL依然會為sql中存在的字段生成直方圖。

生成直方圖時,如果字段的distinct值小于sql指定的bucket,MySQL會選擇生成單值直方圖(singleton histograms),否則生成等高直方圖(equi-height histograms)。

三,直方圖的删除

sql如下:

ANALYZE TABLE tbl_name DROP HISTOGRAM ON col_name [, col_name];
           

随時可以删,不影響資料庫其他操作。

四,直方圖的存儲

生成的直方圖儲存在information_schema.COLUMN_STATISTICS表中,内容是JSON格式。

下面說一下JSON的格式,單值直方圖和等高直方圖的JSON格式略有不同。

以下是一個單值直方圖的JSON例子:

{
  "buckets": [
    [
      1,
      0.3333333333333333
    ],
    [
      2,
      0.6666666666666666
    ],
    [
      3,
      1
    ]
  ],
  "null-values": 0,
  "last-updated": "2017-03-24 13:32:40.000000",
  "sampling-rate": 1,
  "histogram-type": "singleton",
  "number-of-buckets-specified": 128,
  "data-type": "int",
  "collation-id": 8
}
           

以下是一個等高直方圖的例子:

{
  "buckets": [

    [
      38671,
      99756,
      0.249795,
      17002
    ],
    [
      99757,
      100248,
      0.500035,
      492
    ],
    [
      100249,
      100743,
      0.749945,
      495
    ],
    [
      100744,
      172775,
      1.0,
      16630
    ]
  ],
  "data-type": "int",
  "null-values": 0.0,
  "collation-id": 8,
  "last-updated": "2018-09-22 09:59:30.857797",
  "sampling-rate": 1.0,
  "histogram-type": "equi-height",
  "number-of-buckets-specified": 4
}
           

二者在buckets參數中存儲的内容不一樣,其他部分的格式是一樣的。

在單值直方圖的buckets中,有2個值:

  • 第1個值:bucket代表的值。類型由字段類型決定。
  • 第2個值:自己的bucket和前面的bucket中的值的占比之和,是一個逐漸增加的值。取值在0.0到1.0之間。顯然最後一個bucket中比例一定是1。

在等高直方圖的buckets中,有4個值:

  • 第1個值:bucket包括的值的範圍的下限。
  • 第2個值:bucket包括的值的範圍的上限。
  • 第3個值:自己bucket取值範圍和前面bucket取值範圍中的值的占比之和,是一個逐漸增加的值。取值在0.0到1.0之間。同單值直方圖的第2個值,最後一個bucket的比例是1。
  • 第4個值:bucket包括的值的distinct數。

在buckets之外的參數:

  • null-values:null值比例。取值在0.0到1.0之間。0代表沒有null值。
  • last-updated:直方圖更新時間。格式:YYYY-MM-DD hh:mm:ss.uuuuuu。
  • sampling-rate:取樣比例。取值在0.0到1.0之間。1.0代表bucket值完全覆寫字段值,沒有取樣。
  • histogram-type:直方圖類型。singleton代表單值直方圖,每個bucket都代表一個值,字段distinct值小于sql指定bucket數時使用該類型。equi-height代表等高直方圖,每個bucket都代表一個值的範圍,字段distinct值大于sql指定bucket數時使用該類型。
  • number-of-buckets-specified:生成直方圖的sql語句中指定的bucket數量。
  • data-type:直方圖中的資料類型。從磁盤中讀取并存入記憶體中時會用到。可能的值有:int,uint (unsigned integer),double,decimal,datetime,string (包括character and binary strings)。
  • collation-id:直方圖資料中的字元集id。和INFORMATION_SCHEMA.COLLATIONS表中id對應。直方圖中的資料是String類型時會有用。

直方圖中的值可以用MySQL8.0加入的JSON文法獲得。

五,舉例說明直方圖的作用

官方舉了這麼一個例子:

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)
           

這個sql是用來比較上午9點和下午15點的銷售情況,可見執行時間是1.48秒。

然後給wp_char_count建一個直方圖再查一遍:

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)
           

可見查詢隻用了0.5秒,效率提高非常多,原因是這樣的:

建直方圖的字段就是sql中取值5000到5200的字段,在沒有直方圖提供統計資訊的情況下,MySQL預估該字段值在5000到5200的大概占總比例的11.11%,但實際上此範圍内的資料隻占總比例的1.6%:

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)
           

是以,有了直方圖提供的準确統計資訊,優化器會調整優化政策,提前對該表進行join操作,進而提高了查詢效率。

六,直方圖和索引

使用索引也可以把查詢效率提高不少,效果不比直方圖差,二者的差別有:

1,維護索引是有成本的。新增,删除,修改字段時同時需要維護索引,影響效率。對比之下直方圖一經建立就不再修改,不會影響操作效率(顯然是把雙刃劍,會存在資料不準确的情況)。

2,使用索引時,優化器使用index dives的方式預估大概範圍,這個操作也是有成本的。特别是in語句且清單有大量值時,直方圖的效果更強一點。

七,獲得直方圖的資訊

1,擷取更新時間

官方提供了這樣的例子,可以擷取直方圖的生成時間或更新時間:

mysql> SELECT
    ->   HISTOGRAM->>'$."last-updated"' AS last_updated
    -> FROM INFORMATION_SCHEMA.COLUMN_STATISTICS
    -> WHERE
    ->   SCHEMA_NAME = "sakila"
    ->   AND TABLE_NAME = "payment"
    ->   AND COLUMN_NAME = "amount";
+----------------------------+
| last_updated               |
+----------------------------+
| 2017-09-15 11:54:25.000000 |
+----------------------------+
           

2,查詢直方圖bucket

官方提供了這樣的例子:

mysql> SELECT
    ->   TABLE_NAME,
    ->   COLUMN_NAME,
    ->   HISTOGRAM->>'$."number-of-buckets-specified"' AS num_buckets_specified,
    ->   JSON_LENGTH(HISTOGRAM, '$.buckets') AS num_buckets_created
    -> FROM INFORMATION_SCHEMA.COLUMN_STATISTICS
    -> WHERE
    ->   SCHEMA_NAME = "sakila";
+------------+--------------+-----------------------+---------------------+
| TABLE_NAME | COLUMN_NAME  | num_buckets_specified | num_buckets_created |
+------------+--------------+-----------------------+---------------------+
| payment    | amount       | 32                    |                  19 |
| payment    | payment_date | 32                    |                  32 |
+------------+--------------+-----------------------+---------------------+
           

其中,'$."number-of-buckets-specified"'是生成直方圖的sql中指定的bucket數量,

JSON_LENGTH(HISTOGRAM, '$.buckets')是生成的直方圖中實際的bucket數量。

實際bucket數不會超過sql中指定的bucket上限。

使用直方圖時要注意的點

通過前面直方圖和索引的比較可以看到,直方圖自生成之後就不再随資料變化而改變,除非手動重做直方圖。

這樣帶來的資料不準确的問題是需要開發者自己考慮的。比如什麼時候需要重新整理直方圖,以頻率重新整理等。

因為生成直方圖消耗的cpu和記憶體資源都挺大,使用的時候要注意。

MySQL8.0全部學習筆記:

MySQL8.0新特性學習筆記(一):binlog複制政策優化

MySQL8.0新特性學習筆記(二):視窗函數

MySQL8.0新特性學習筆記(三):直方圖

MySQL8.0新特性學習筆記(四):Hash Join

MySQL8.0新特性學習筆記(五):JSON格式簡介和JSON函數詳解

MySQL8.0新特性學習筆記(六):新特性介紹