MySQL 8.0 中統計信息直方圖的嘗試

 

直方圖是表上某個字段在按照必定百分比和規律採樣後的數據分佈的一種描述,最重要的做用之一就是根據查詢條件,預估符合條件的數據量,爲sql執行計劃的生成提供重要的依據
在MySQL 8.0以前的版本中,MySQL僅有一個簡單的統計信息卻沒有直方圖,沒有直方圖的統計信息能夠說是沒有任何意義的。
MySQL 8.0新特性之一就是開始支持統計信息的直方圖,這個概念很早就提出來了,抽空具體嘗試了一下使用方法。html

以前寫過MSSQL相關統計信息的一點東西,在原理上都是一致的,http://www.javashuo.com/article/p-wnvqeawk-kb.htmlmysql

 

照舊,直接上例子,造數據,建立一個測試環境算法

create table test
(
    id int auto_increment primary key,
    name varchar(100),
    create_date datetime ,
    index (create_date desc)
);


USE `db01`$$

DROP PROCEDURE IF EXISTS `insert_test_data`$$

CREATE DEFINER=`root`@`%` PROCEDURE `insert_test_data`()
BEGIN
    DECLARE v_loop INT;
    SET v_loop = 100000;
    WHILE v_loop>0 DO
        INSERT INTO test(NAME,create_date)VALUES (UUID(),DATE_ADD(NOW(),INTERVAL -RAND()*100000 MINUTE) );
        SET v_loop = v_loop - 1;
    END WHILE;
END$$

DELIMITER ;

MySQL中統計信息的建立,不一樣於MSSQL,MySQL統計信息不依賴於索引,須要單首創建,語法以下sql

--建立字段上的統計直方圖信息
ANALYZE TABLE test UPDATE HISTOGRAM ON create_date,name WITH 16 BUCKETS;
--刪除字段上的統計直方圖信息
ANALYZE TABLE test DROP HISTOGRAM ON create_datejson


1,能夠一次性建立多個字段的統計信息,系統會逐個建立列出的字段上的統計信息,統計信息不依賴於索引,這一點與MSSQL不一樣(固然MSSQL也能夠拋開索引獨立建立統計信息)
2,BUCKETS值是一個必須提供的參數,默認值爲1000,範圍是1-1024,這一點也不一樣與MSSQL也不同,MSSQL是有一個相似的最大值爲200的步長(step)字段
3,通常來講,數據量較大的狀況下,對於不重複或者重複性不高的數據,BUCKETS值越大,描述出來的統計信息越詳細
4,統計信息的具體內容在 information_schema.column_statistics中,可是可讀性並很差,能夠根據需求自行解析(出來一種本身喜歡的格式)服務器

與sqlserver中的統計信息同樣,理論上,在準確性與取樣百分比(BUCKETS)是成正比的,固然生成統計信息的代價也就越大,
至於BUCKETS與統計信息的取樣百分比,以及綜合代價,筆者暫時沒有找到相關的資料。less

以下是經過ANALYZE TABLE test UPDATE HISTOGRAM ON create_date WITH 4 BUCKETS;建立的統計信息直方圖
能夠發現直方圖的HISTOGRAM字段是一個JSON格式的字符串,可讀性並很差。oop

想到了sqlserver中DBCC SHOW_STATISTICS的直方圖信息,以下的格式,直方圖中的數據分佈狀況看起來很是清晰直觀sqlserver

因而就作了一個MySQL直方圖的格式轉換,說白了就是解析information_schema.column_statistics表中的HISTOGRAM 字段中的JSON內容
以下,一個簡單的解析直方圖統計信息json數據的存儲過程,參數分別是庫名,表名,字段名測試

DELIMITER $$

USE `db01`$$

DROP PROCEDURE IF EXISTS `parse_column_statistics`$$

CREATE DEFINER=`root`@`%` PROCEDURE `parse_column_statistics`(
    IN `p_schema_name` VARCHAR(200),
    IN `p_table_name` VARCHAR(200),
    IN `p_column_name` VARCHAR(200)
)
BEGIN
    
    DECLARE v_histogram TEXT;
    -- get the special HISTOGRAM
    SELECT HISTOGRAM->>'$."buckets"' INTO v_HISTOGRAM 
    FROM   information_schema.column_statistics
    WHERE schema_name =  p_schema_name 
    AND table_name = p_table_name 
    AND column_name = p_column_name; 
    
    -- remove the first and last [ and ] char
    SET v_histogram = SUBSTRING(v_HISTOGRAM,2,LENGTH(v_HISTOGRAM)-2);
DROP TABLE IF EXISTS t_buckets ; CREATE TEMPORARY TABLE t_buckets ( id INT AUTO_INCREMENT PRIMARY KEY, buckets_content VARCHAR(500) ); -- split by "]," and get single bucket content WHILE (INSTR(v_histogram,'],')>0) DO INSERT INTO t_buckets(buckets_content) SELECT SUBSTRING(v_histogram,1,INSTR(v_histogram,'],')); SET v_HISTOGRAM = SUBSTRING(v_histogram,INSTR(v_histogram,'],')+2,LENGTH(v_histogram)); END WHILE;
  
INSERT INTO t_buckets(buckets_content) SELECT v_histogram; -- get the basic statistics data WITH cte AS ( SELECT HISTOGRAM->>'$."last-updated"' AS last_updated, HISTOGRAM->>'$."number-of-buckets-specified"' AS number_of_buckets_specified FROM INFORMATION_SCHEMA.COLUMN_STATISTICS WHERE schema_name = p_schema_name AND table_name = p_table_name AND column_name = p_column_name ) SELECT CASE WHEN id = 1 THEN p_schema_name ELSE '' END AS schema_name, CASE WHEN id = 1 THEN p_table_name ELSE '' END AS table_name, CASE WHEN id = 1 THEN p_column_name ELSE '' END AS column_name, CASE WHEN id = 1 THEN last_updated ELSE '' END AS last_updated, CASE WHEN id = 1 THEN number_of_buckets_specified ELSE '' END AS 'number_of_buckets_specified' , id AS buckets_specified_index, buckets_content FROM ( SELECT * FROM cte,t_buckets )t; END$$ DELIMITER ;

因而,第一個截圖中的結果就轉換爲了以下的格式
這裏刻意按照4個buckets生成的直方圖,應該來講足夠簡單了,熟悉MSSQL直方圖同窗,應該一眼就能夠看明白這個直方圖的含義(測試數據量是400,000)
以第一個bucket爲例:["2018-06-15 04:57:48.000000", "2018-07-02 15:13:04.000000", 0.25, 95311]

很明顯,
1,"2018-06-15 04:57:48.000000"和"2018-07-02 15:13:04.000000"是相似於sqlserver中直方圖中的下限值與上限值
2,0.25小於bucket的值的比例(也就小於這個區間上限制值的比例)
3,95311是這個區間的字段值不重複的行數。
到最後一個bucket,採樣率必然是1,也就是100%

須要注意的是,直方圖的更新時間是標準時間(UTC value),而不是服務器當前時間。
MySQL 8.0中的直方圖基本上與sqlserver的直方圖一致,都是基於單列的抽樣預估,可是MySQL直方圖中沒有相似於sqlserver中的字段選擇性,
不過這個字段選擇性自己意義也不大 ,sqlserver中對於複合索引,兩個字段合計在一塊統計,除非兩個字段的同時分佈的都很均勻,不然多字段索引的字段選擇性參考意義不大。
這也是複合索引沒法作到較爲精確預估的緣由。

 

存在的疑問?

以前寫過一點MySQL統計信息的,不過是在MySQL5.7下面,尚未直方圖的概念http://www.javashuo.com/article/p-mqirlxtr-km.html
觸發統計信息更新的變量仍是set global innodb_stats_on_metadata = 1;可是經測試,統計信息的直方圖並無所以而更新。
innodb_stats_on_metadata在MySQL5.7中影響到的是MySQL的索引上的統計信息,而這裏純粹是統計信息的直方圖(MySQL 8.0中直方圖跟索引沒有必然的關係)。
另外,這裏通過反覆測試發現,buckets的數據量,與生成直方圖的效率並無很是明顯的關係,以下截圖,也並不清楚,buckets數量跟取樣百分比有什麼關係。

又仔細看了一下參考連接的內容,發現這麼一段話:

  1. Maintaining an index has a cost. If you have an index, every INSERT/UPDATE/DELETE causes the index to be updated. This is not free, and will have an impact on your performance. A histogram on the other hand is created once and never updated unless you explicitly ask for it. It will thus not hurt your INSERT/UPDATE/DELETE-performance.

   它自己是說明索引與直方圖之間的關係的,提到直方圖建立以後並不會自動更新,除非主動更新。

不得不吐槽的就是,若是我在某個字段上建立了一個索引,還須要順便在建立一個統計信息直方圖?而且這個直方圖並不會隨着數據的變化自動更新,還須要手動更新。
MySQL 8.0中會不會把統計信息和索引關聯起來,或者根據須要自動建立統計信息,若是統計信息作不到自動更新,基本上能夠認爲是殘廢的統計信息了。

 

關於生成直方圖中時的資源的消耗

直方圖的生成是一個比較消耗資源的過程的,以下是在反覆測試建立直方圖的過程當中,zabbix監控到的服務器的CPU使用狀況,固然,這裏僅僅觀察了一下CPU使用率的問題。
所以,直方圖再好,真要大規模應用的使用,仍是要綜合考量的,在何時執行更新,以及怎麼去觸發它的更新。

這裏僅僅是粗淺嘗試,不免有不少認識不足的地方。

 

一些有意思的東西

本文最後給出的參考連接中發現一些有意思的東西
MySQL 8.0中一些有意思的預估算法,看來看去,跟sqlserver中的差異不大,都是相似大概這幾種算法,算是沒有辦法的辦法了。
對於兩個謂詞結合在一塊兒時候的預估,或者是沒有統計信息覆蓋的預估,基本上能夠認爲是瞎蒙的,所以上文中也提到,多個謂詞結合起來的選擇性,沒有什麼意義。

------------------------------------
AND       : P(A and B) = P(A) * P(B)
OR        : P(A or B)  = P(A) + P(B) - P(A and B)
=         : 1/10
<,>       : 1/3
BETWEEN   : 1/4
IN (list) : MIN(#items_in_list * SEL(=), 1/2)
IN subq   : [1]
NOT OP    : 1-SEL(OP)

與此相似的,sqlserver中的預估算法:
http://www.javashuo.com/article/p-xbdelaif-ks.html
http://www.javashuo.com/article/p-czctqiog-ky.html
http://www.javashuo.com/article/p-mjjrkuyq-me.html

 

 

參考:
https://mysqlserverteam.com/histogram-statistics-in-mysql/
https://dev.mysql.com/doc/refman/8.0/en/optimizer-statistics.html
https://dev.mysql.com/doc/refman/8.0/en/analyze-table.html#analyze-table-histogram-statistics-analysis

相關文章
相關標籤/搜索