浅析MySQL 8.0直方图原理
本文分享自华为云社区《【MySQL技术专栏】MySQL8.0直方图介绍》,作者:GaussDB 数据库。
背景
数据库查询优化器负责将SQL查询转换为尽可能高效的执行计划,但因为数据环境不断变化导致优化器对查询数据了解的不够充足,可能无法生成最优的执行计划进而影响查询效率,因此MySQL8.0推出了直方图(histogram)功能来解决该问题。
直方图用于统计字段值的分布情况,向优化器提供统计信息。利用直方图,可以对一张表的一列数据做分布统计,估算where条件中过滤字段的选择率,从而帮助优化器更准确地估计查询过程中的行数,选择更高效的查询计划。
本文将对直方图概念进行介绍,借助举例描述直方图的使用方式,对创建/删除直方图的原理进行浅析,并通过例子说明其应用场景。
MySQL8.0直方图介绍
数据库中,查询优化器所生成执行计划的好坏关乎执行耗时的多少,优化器若是不清楚表中数据的分布情况,可能会导致无法生成最优的执行计划,造成执行时浪费时间。
假设一条SQL语句要查询相等间隔的两个不同时间段内出行的人数,若不知道每个时间段内的人数,优化器会假设人数在两个不同时间段内是均匀分布的。如果两个时间段内人数相差较大,这样优化器估算的统计数据就出现严重偏差,从而可能选择错误的执行计划。那么,如何使优化器比较清楚地知道数据统计情况进而生成好的执行计划呢?
一种解决方法就是,在列上建立直方图,从而近似地获取一列上的数据分布情况。利用好直方图,将会带来很多方面收益:
(1)查询优化:提供关于数据分布的统计信息,帮助优化查询计划,选择合适的索引和优化查询语句,从而提高查询性能;
(2)索引设计:通过分析数据的分布情况,帮助确定哪些列适合创建索引,以提高查询效率;
(3)数据分析:提供数据的分布情况,帮助用户了解数据的特征和趋势。
直方图分为两类:等宽直方图(singleton)和等高直方图(equi-height)。等宽直方图是每个桶保存一个值以及这个值累积频率:
SCHEMA_NAME: xxx//库名
TABLE_NAME: xxx//表名
COLUMN_NAME: xxx//列名
HISTOGRAM: {
"buckets":[
[
xxx, //桶中数值
xxx //取值频率
],
......
],
"data-type":"xxx", //数据类型
"null-values":xxx, //是否有NULL值
"collation-id":xxx,
"last-updated":"xxxx-xx-xx xx:xx:xx.xxxxxx", //更新时间
"sampling-rate":xxx, //采样率,1表示采集所有数据
"histogram-type":"singleton", //桶类型,等宽
"number-of-buckets-specified":xxx //桶数量
}
等高直方图每个桶需要保存不同值的个数,上下限以及累积频率等:
SCHEMA_NAME: xxx
TABLE_NAME: xxx
COLUMN_NAME: xxx
HISTOGRAM: {
"buckets":[
[
xxx, //最小值
xxx, //最大值
xxx, //桶值出现的频率
xxx //桶值出现的次数
],
......
],
"data-type":"xxx",
"null-values":xxx,
"collation-id":xxx,
"last-updated":"xxxx-xx-xx xx:xx:xx.xxxxxx",
"sampling-rate":xxx,
"histogram-type":"equi-height", //桶类型,等高
"number-of-buckets-specified":xxx
}
MySQL8.0直方图使用方式
创建和删除直方图时涉及analyze语句,常用语法格式为:
创建直方图:
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> create table t1(c1 int,c2 int,c3 int,c4 int,c5 int,c6 int,c7 int,c8 int,c9 int,c10 int,c11 int,c12 int,c13 datetime,c14 int,c15 int,c16 int,primary key(c1));
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t1 values(1,2,3,4,5,6,7,8,9,10,11,12,'0000-01-01',14,15,16),(2,2,3,4,5,6,7,8,9,10,11,12,'0500-01-01',14,15,16),(3,2,3,4,5,6,7,8,9,10,11,12,'1000-01-01',14,15,16),(4,2,3,4,5,6,7,8,9,10,11,12,'1500-01-01',14,15,16),(5,2,3,4,5,6,7,8,9,10,11,12,'1500-01-01',14,15,16);
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
创建直方图:
mysql> analyze table t1 update histogram on c13;
+---------+-----------+----------+------------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+---------+-----------+----------+------------------------------------------------+
| test.t1 | histogram | status | Histogram statistics created for column 'c13'. |
+---------+-----------+----------+------------------------------------------------+
1 row in set (0.01 sec)
查看直方图信息:
mysql> select json_pretty(histogram)result from information_schema.column_statistics where table_name = 't1' and column_name = 'c13'\G
*************************** 1. row ***************************
result: {
"buckets": [
[
"0000-01-01 00:00:00.000000", //统计的列值
0.2 //统计的相对频率,下同
],
[
"0500-01-01 00:00:00.000000",
0.4
],
[
"1000-01-01 00:00:00.000000",
0.6
],
[
"1500-01-01 00:00:00.000000",
1.0
]
],
"data-type": "datetime", //统计的数据类型
"null-values": 0.0, //NULL值的比例
"collation-id": 8, //直方图数据的排序规则ID
"last-updated": "2023-09-30 16:05:28.533732", //最近更新直方图的时间
"sampling-rate": 1.0, //直方图构建采样率
"histogram-type": "singleton", //直方图类型,等宽
"number-of-buckets-specified": 100 //桶数量
}
1 row in set (0.00 sec)
删除直方图:
mysql> analyze table t1 drop histogram on c13;
+---------+-----------+----------+------------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+---------+-----------+----------+------------------------------------------------+
| test.t1 | histogram | status | Histogram statistics removed for column 'c13'. |
+---------+-----------+----------+------------------------------------------------+
1 row in set (0.00 sec)
MySQL8.0直方图原理浅析
直方图原理整体框架可概括为下图所示:
直方图代码主要包含在sql/histograms路径下,带有equi_height前缀的相关文件涉及等高直方图,带有singleton前缀的相关文件涉及等宽直方图,带有value_map前缀的相关文件涉及保存统计值结构,histogram.h/histogram.cc涉及直方图相关调用接口。
Sql_cmd_analyze_table::handle_histogram_command为对直方图操作的整体处理入口,目前只支持在一张表上进行直方图相关操作。创建直方图的主要调用堆栈如下所示,update_histogram为创建直方图的入口。
mysql_execute_command
->Sql_cmd_analyze_table::execute
->Sql_cmd_analyze_table::handle_histogram_command
->Sql_cmd_analyze_table::update_histogram
->histograms::update_histogram
->prepare_value_maps
->fill_value_maps
->build_histogram
->store_histogram
->dd::cache::Dictionary_client::update
->dd::cache::Storage_adapter::store
->dd::Column_statistics_impl::store_attributes
->histograms::Singleton<xxx>::histogram_to_json
对于创建流程展开描述,prepare_value_maps中主要根据直方图列类型创建对应的value_map做准备,之后利用histogram_generation_max_mem_size参数值(限制生成直方图时所允许使用的最大内存大小)和单行数据大小计算后控制统计采样率,fill_value_maps将反复读取数据填充到对应类型的value_map中,key为列实际值,value为其出现的次数。调用build_histogram以完成对直方图的构建,如果桶个数(num_buckets)比不同值个数(value_map.size())要大,则自动创建一个等宽直方图,否则创建一个等高直方图。两种直方图的创建逻辑分别在Singleton<T>:: build_histogram和Equi_height<T>:: build_histogram中。
构建直方图完成后调用store_histogram,将结果以JSON的形式存储在系统表中,通过INFORMATION_SCHEMA.COLUMN_STATISTICS对用户呈现,histogram_to_json会将直方图结果转换为Json_object格式,例如last-updated使用Json_datetime格式保存、histogram-type使用Json_string格式保存、sampling rate使用Json_double格式保存等,再依次调用json_object->add_clone将各json类型字段保存。
删除直方图的主要堆栈如下所示。drop_histograms逻辑中在删除直方图前会先尝试获取以检查对应直方图是否真的存在,不存在的话就提前终止逻辑,存在则删除。
mysql_execute_command
->Sql_cmd_analyze_table::execute
->Sql_cmd_analyze_table::handle_histogram_command
->Sql_cmd_analyze_table::update_histogram
->histograms::update_histogram
MySQL8.0直方图优化场景
优化方面,如本文在前所描述的直方图作用,利用直方图信息估算where条件中各谓词的选择率,帮助选择最优的执行计划。例如,表存在如下所示数据倾斜场景。
mysql> select sys_id,order_status,count(*) from my_table_1 group by sys_id,order_status order by 1,2,3;
+--------+--------------+----------+
| sys_id | order_status | count(*) |
+--------+--------------+----------+
| 3 | 1 | 1 |
| 3 | 2 | 200766 |
| 3 | 3 | 3353 |
| 3 | 4 | 1325 |
| 5 | 1 | 13 |
| 5 | 2 | 2478373 |
| 5 | 3 | 43243 |
| 5 | 4 | 13529 |
| 6 | 2 | 171388 |
| 6 | 3 | 254 |
| 6 | 4 | 716 |
+--------+--------------+----------+
执行如下SQL语句时,因为存在数据倾斜而优化器未能准确估计导致执行计划选择错误,执行耗时约为1.35s。
mysql> explain analyze select t1.id, t1.order_number, t1.create_time, t1.order_status from my_table_1 t1 left join my_table_2 t2 on t1.id = t2.order_id WHERE t1.sys_id = 5 and t1.order_status in (1) and t1.create_time >= '2022-09-10 00:00:00' and t1.create_time <= '2022-09-16 23:59:59' order by t1.id desc LIMIT 20\G
*************************** 1. row ***************************
EXPLAIN: -> Limit: 20 row(s) (cost=4163.10 rows=20) (actual time=1350.825..1350.825 rows=0 loops=1)
-> Nested loop left join (cost=4163.10 rows=49) (actual time=1350.825..1350.825 rows=0 loops=1)
-> Filter: ((t1.order_status = 1) and (t1.sys_id = 5) and (t1.create_time >= TIMESTAMP'2022-09-10 00:00:00') and (t1.create_time <= TIMESTAMP'2022-09-16 23:59:59')) (cost=215.79 rows=49) (actual time=1350.823..1350.823 rows=0 loops=1)
-> Index scan on t1 using PRIMARY (reverse) (cost=215.79 rows=8828) (actual time=0.088..1209.201 rows=2910194 loops=1)
-> Index lookup on t2 using idx_order_id (order_id=t1.id) (cost=0.63 rows=1) (never executed)
通过执行ANALYZE table my_table_1 UPDATE HISTOGRAM ON order_status, sys_id, create_time语句创建直方图后,再次执行上述SQL语句时,执行计划中的索引发生了变化,执行耗时为0.11s。因此可以看出,优化器利用更准确的数据分布信息选择了更优的执行计划。
mysql> explain analyze select t1.id, t1.order_number, t1.create_time, t1.order_status from my_table_1 t1 left join my_table_2 t2 on t1.id = t2.order_id WHERE t1.sys_id = 5 and t1.order_status in (1) and t1.create_time >= '2022-09-10 00:00:00' and t1.create_time <= '2022-09-16 23:59:59' order by t1.id desc LIMIT 20\G
*************************** 1. row ***************************
EXPLAIN: -> Limit: 20 row(s) (cost=38385.46 rows=20) (actual time=114.217..114.217 rows=0 loops=1)
-> Nested loop left join (cost=38385.46 rows=62764) (actual time=114.216..114.216 rows=0 loops=1)
-> Sort: t1.id DESC, limit input to 20 row(s) per chunk (cost=28200.86 rows=62668) (actual time=114.215..114.215 rows=0 loops=1)
-> Filter: (t1.order_status = 1) (cost=28200.86 rows=62668) (actual time=114.207..114.207 rows=0 loops=1)
-> Index range scan on t1 using idx_sys_id_create_time, with index condition: ((t1.sys_id = 5) and (t1.create_time >= TIMESTAMP'2022-09-10 00:00:00') and (t1.create_time <= TIMESTAMP'2022-09-16 23:59:59')) (cost=28200.86 rows=62668) (actual time=0.326..112.912 rows=31142 loops=1)
-> Index lookup on t2 using idx_order_id (order_id=t1.id) (cost=0.62 rows=1) (never executed)
另外,当where条件中变量值不同时,优化器也根据数据分布情况选择了准确的执行计划,使得执行效率提高。
mysql> explain format=tree select t1.id, t1.order_number, t1.create_time, t1.order_status from my_table_1 t1 left join my_table_2 t2 on t1.id = t2.order_id WHERE t1.sys_id = 5 and t1.order_status in (2) and t1.create_time >= '2020-10-01 00:00:00' and t1.create_time <= '2020-10-09 23:59:59' order by t1.id desc LIMIT 20\G
*************************** 1. row ***************************
EXPLAIN: -> Limit: 20 row(s) (cost=13541.27 rows=20)
-> Nested loop left join (cost=13541.27 rows=44)
-> Filter: ((t1.order_status = 2) and (t1.sys_id = 5) and (t1.create_time >= TIMESTAMP'2020-10-01 00:00:00') and (t1.create_time <= TIMESTAMP'2020-10-09 23:59:59')) (cost=15.79 rows=44)
-> Index scan on t1 using PRIMARY (reverse) (cost=15.79 rows=338)
-> Index lookup on t2 using idx_order_id (order_id=t1.id) (cost=0.25 rows=1)
1 row in set (0.00 sec)
mysql> explain format=tree select t1.id, t1.order_number, t1.create_time, t1.order_status from my_table_1 t1 left join my_table_2 t2 on t1.id = t2.order_id WHERE t1.sys_id = 5 and t1.order_status in (4) and t1.create_time >= '2020-10-01 00:00:00' and t1.create_time <= '2020-10-09 23:59:59' order by t1.id desc LIMIT 20\G
*************************** 1. row ***************************
EXPLAIN: -> Limit: 20 row(s) (cost=30559.31 rows=20)
-> Nested loop left join (cost=30559.31 rows=55852)
-> Sort: t1.id DESC, limit input to 20 row(s) per chunk (cost=24966.26 rows=55480)
-> Filter: (t1.order_status = 4) (cost=24966.26 rows=55480)
-> Index range scan on t1 using idx_sys_id_create_time, with index condition: ((t1.sys_id = 5) and (t1.create_time >= TIMESTAMP'2020-10-01 00:00:00') and (t1.create_time <= TIMESTAMP'2020-10-09 23:59:59')) (cost=24966.26 rows=55480)
-> Index lookup on t2 using idx_order_id (order_id=t1.id) (cost=0.25 rows=1)
1 row in set (0.00 sec)
所以,通过所提供的统计信息,帮助优化查询计划进而提高查询性能是如前所述应用直方图的一个收益点。
浅析MySQL 8.0直方图原理的更多相关文章
- MySQL 8.0 中统计信息直方图的尝试
直方图是表上某个字段在按照一定百分比和规律采样后的数据分布的一种描述,最重要的作用之一就是根据查询条件,预估符合条件的数据量,为sql执行计划的生成提供重要的依据在MySQL 8.0之前的版本中,My ...
- 浅析Mysql InnoDB存储引擎事务原理
浅析Mysql InnoDB存储引擎事务原理 大神:http://blog.csdn.net/tangkund3218/article/details/47904021
- 详谈 MySQL 8.0 原子 DDL 原理
柯煜昌 青云科技研发顾问级工程师 目前从事 RadonDB 容器化研发,华中科技大学研究生毕业,有多年的数据库内核开发经验. 文章字数 3800+,阅读时间 15 分钟 背景 MySQL 5.7 的字 ...
- 浅析MySQL数据碎片的产生(data free)
浅析MySQL数据碎片的产生 2011-03-30 09:28 核子可乐译 51CTO 字号:T | T MySQL列表,包括MyISAM和InnoDB这两种最常见的类型,而根据经验来说,其碎片的产生 ...
- 浅析MySQL复制
MySQL的复制是基于binlog来实现的. 流程如下 涉及到三个线程,主库的DUMP线程,从库的IO线程和SQL线程. 1. 主库将所有操作都记录到binlog中.当复制开启时,主库的DUMP线程根 ...
- 十种MYSQL显错注入原理讲解(二)
上一篇讲过,三种MYSQL显错注入原理.下面我继续讲解. 1.geometrycollection() and geometrycollection((select * from(select * f ...
- Mysql报错注入原理分析(count()、rand()、group by)
Mysql报错注入原理分析(count().rand().group by) 0x00 疑问 一直在用mysql数据库报错注入方法,但为何会报错? 百度谷歌知乎了一番,发现大家都是把官网的结论发一下截 ...
- MySQL备份恢复-mysqldump原理
+++++++++++++++++++++++++++++++++++++++++++标题:mysqldump对MySQL数据库备份恢复原理时间:2019年2月23日内容:mysqldump工具重点: ...
- MySQL 8.0 —— CATS事务调度算法的性能提升
原文地址:https://mysqlserverteam.com/contention-aware-transaction-scheduling-arriving-in-innodb-to-boost ...
- MySQL 并行复制演进及 MySQL 8.0 中基于 WriteSet 的优化
MySQL 8.0 可以说是MySQL发展历史上里程碑式的一个版本,包括了多个重大更新,目前 Generally Available 版本已经已经发布,正式版本即将发布,在此将介绍8.0版本中引入的一 ...
随机推荐
- Qt Create开发,修改 .Pro 文件改变 exe 的名称
// .pro // 修改 TARGET 就可以改变生成的exe的名称 TARGET = Test // 要是生成的exe名称中需要带有空格,需要用到$$quote TARGET = $$quote( ...
- gitlib配置
第1步:下载Java并安装Java.一般来说下载jdk对应的版本就行. 第2步:配置Java环境变量,具体如下: 1. 新建:变量名:JAVA_HOME:变量值:C:\Program Files\Ja ...
- MMDeploy部署实战系列【第四章】:onnx,tensorrt模型推理
MMDeploy部署实战系列[第四章]:onnx,tensorrt模型推理 这个系列是一个随笔,是我走过的一些路,有些地方可能不太完善.如果有那个地方没看懂,评论区问就可以,我给补充. 目录: 0️⃣ ...
- js扩展方法(自用)
//字符串转Date 字符串格式 yyyy-MM-dd HH:mm:ssString.prototype.toDate = function() { var date = eval('new Date ...
- javascript现代编程系列教程之二——IIFE
IIFE(Immediately Invoked Function Expression,立即执行函数表达式)是一个在定义后立即执行的JavaScript函数.它具有以下特点: 是一个匿名函数:通常情 ...
- kratos http原理
概念 kratos 为了使http协议的逻辑代码和grpc的逻辑代码使用同一份,选择了基于protobuf的IDL文件使用proto插件生成辅助代码的方式. protoc http插件的地址为:htt ...
- 红日安全vulnstack (一)
网络拓扑图 靶机参考文章 CS/MSF派发shell 环境搭建 IP搭建教程 本机双网卡 65网段和83网段是自己本机电脑(虚拟机)中的网卡, 靶机外网的IP需要借助我们这两个网段之一出网 Kali ...
- 力扣610(MySQL)-判断三角形(简单)
题目: 表: Triangle 写一个SQL查询,每三个线段报告它们是否可以形成一个三角形. 以 任意顺序 返回结果表. 查询结果格式如下所示. 示例1: 解题思路: 判断是否形成三角形的准则是:两 ...
- 实时化或成必然趋势?新一代 Serverless 实时计算引擎
作者:高旸(吾与),阿里巴巴高级产品专家 本文由阿里巴巴高级产品专家高旸(吾与)分享,主要介绍新一代Serverless实时计算引擎的产品特性及核心功能. 一.实时计算 Flink 版 – 产品定位与 ...
- [Docker] 镜像源配置 for Linux
$ vi /etc/docker/daemon.json { "registry-mirrors": [ "https://docker.mirrors.ustc.edu ...