【GreatSQL优化器-13】直方图

news/2025/2/24 2:10:14/文章来源:https://www.cnblogs.com/greatsql/p/18714991

【GreatSQL优化器-13】直方图

一、直方图介绍

GreatSQL的优化器负责将SQL查询转换为尽可能高效的执行计划,但因为数据环境不断变化有可能导致优化器对查询数据了解不够充足,可能无法生成最优的执行计划进而影响查询效率,因此推出了直方图(histogram)功能来解决该问题。

直方图用于统计字段值的分布情况,向优化器提供统计信息。利用直方图,可以对一张表的一列数据做分布统计,估算WHERE条件中过滤字段的选择率,从而帮助优化器更准确地估计查询过程中的行数,选择更高效的查询计划。

直方图以灵活的JSON的格式存储。ANALYZE TABLE会基于表大小自动判断是否要进行取样操作。

ANALYZE TABLE也会基于表中列的数据分布情况以及bucket的数量来决定是否要建立等宽直方图(singleton)还是等高直方图(equi-height)。

下面用一个简单的例子来说明直方图是什么。

greatsql> CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT,date1 DATETIME);
greatsql> INSERT INTO t1 VALUES (1,10,'2021-03-25 16:44:00.123456'),(2,1,'2022-03-26 16:44:00.123456'),(3,4,'2023-03-27 16:44:00.123456'),(5,5,'2024-03-25 16:44:00.123456'),(7,null,'2020-03-25 16:44:00.123456'),(8,10,'2020-10-25 16:44:00.123456'),(11,16,'2023-03-25 16:44:00.123456');
greatsql> CREATE TABLE t2 (cc1 INT PRIMARY KEY, cc2 INT);
greatsql> INSERT INTO t2 VALUES (1,3),(2,1),(3,2),(4,3),(5,15);
greatsql> CREATE TABLE t3 (ccc1 INT, ccc2 varchar(100));
greatsql> INSERT INTO t3 VALUES (1,'aa1'),(2,'bb1'),(3,'cc1'),(4,'dd1'),(null,'ee');
greatsql> CREATE INDEX idx1 ON t1(c2);
greatsql> CREATE INDEX idx2 ON t1(c2,date1);
greatsql> CREATE INDEX idx2_1 ON t2(cc2);
greatsql> CREATE INDEX idx3_1 ON t3(ccc1);系统自动创建buckets:
greatsql> ANALYZE TABLE t1 UPDATE HISTOGRAM ON c2,date1 WITH 3 BUCKETS;
greatsql> SELECT json_pretty(histogram)result from information_schema.column_statistics where table_name = 't1';
| {"buckets": [[1, 最小值5, 最大值0.42857142857142855, 频率3 key个数],[10,10,0.7142857142857143,1],[16,16,0.8571428571428571,1]],"data-type": "int","null-values": 0.14285714285714285,"collation-id": 8,"last-updated": "2024-10-22 08:38:48.858099","sampling-rate": 1.0,"histogram-type": "equi-height","number-of-buckets-specified": 3
}                                                                                                                                     |
| {"buckets": [["2020-03-25 16:44:00.000000","2021-03-25 16:44:00.000000",0.42857142857142855,3],["2022-03-26 16:44:00.000000","2023-03-27 16:44:00.000000",0.8571428571428571,3],["2024-03-25 16:44:00.000000","2024-03-25 16:44:00.000000",1.0,1]],"data-type": "datetime","null-values": 0.0,"collation-id": 8,"last-updated": "2024-10-22 08:38:48.859681","sampling-rate": 1.0,"histogram-type": "equi-height","number-of-buckets-specified": 3
} |用户手动指定buckets:
greatsql> ANALYZE TABLE t2 UPDATE HISTOGRAM ON cc2 USING DATA '{"buckets": [[1, 0.25], [2, 0.5], [3, 0.625], [15, 0.75]], "data-type": "int", "null-values": 0.25, "collation-id": 8, "sampling-rate": 1.0, "histogram-type": "singleton", "number-of-buckets-specified": 4}';
greatsql> select json_pretty(histogram)result from information_schema.column_statistics where table_name = 't2';
| {"buckets": [[1, 值0.25 值占总数百分比],[2,0.5],[3,0.625],[15,0.75]],"data-type": "int","null-values": 0.25,"collation-id": 8,"last-updated": "2024-10-23 02:14:04.474196","sampling-rate": 1.0,"histogram-type": "singleton","number-of-buckets-specified": 4
}

二、update_histogram代码解释

histogram.h/histogram.cc涉及直方图相关调用接口,等高直方图创建在equi_height.cc,等宽直方图创建在singleton.cc

bool update_histogram(THD *thd, Table_ref *table, const columns_set &columns,int num_buckets, LEX_STRING data, results_map &results) {// UPDATE HISTOGRAM指定格式的直方图创建if (data.str != nullptr) {// Convert JSON to histogramhistograms::Histogram *histogram = Histogram::json_to_histogram();// 直方图持久化histogram->store_histogram(thd);}// Read data from the table into the Value_maps we have prepared.// 根据随机抽样原则,从引擎抽样读取数据存入value_map,value_map结构为{唯一值,个数},抽样率计算见表三if (fill_value_maps(resolved_fields, sample_percentage, tbl, value_maps))return true;// 针对每个指定列创建直方图for (const Field *field : resolved_fields) {// 按照下面表一规则创建直方图,把value_map的key值分配到每个桶,分配原则见函数build_histogramvalue_maps.at(field->field_index())->build_histogram();}
}// 等高直方图创建
bool Equi_height<T>::build_histogram(const Value_map<T> &value_map,size_t num_buckets) {for (; freq_it != value_map.end(); ++freq_it) {添加数据到桶的规则:1、该数据不是key值的最后一条2、剩余的key值个数>剩余空桶数量3、添加数据进去不会导致桶大小溢出,因为不知道每个key分别有多少个对应value,这里bucket_max_values用总行数(扣除null值后)进行二分法后用贪婪算法算出来的,一旦发现桶不够用了马上转到下一次二分法重新装数据计算,二分法最多次数10次,因此算出来的桶的高度可能偏大。详细见FindBucketMaxValues函数if (next != value_map.end() &&distinct_values_remaining > empty_buckets_remaining &&bucket_values + next->second <= bucket_max_values) {continue;}// 计算数据个数占总数的百分比,※注意这里的总个数包含null值double cumulative_frequency =cumulative_values / static_cast<double>(total_values);if (m_buckets.push_back(bucket)) return true;}
}// 等宽直方图创建
bool Singleton<T>::build_histogram(const Value_map<T> &value_map,size_t num_buckets) {const ha_rows total_count =value_map.get_num_null_values() + num_non_null_values;for (const auto &node : value_map) {cumulative_sum += node.second;// 按照数据个数求占总数据的百分比,※注意这里的总个数包含null值const double cumulative_frequency =cumulative_sum / static_cast<double>(total_count);m_buckets.push_back(SingletonBucket<T>(node.first, cumulative_frequency));}                               
}

表一:直方图类型创建规则

直方图 类型 说明 使用场合
等宽 singleton 每个桶保存一个值以及这个值累积频率 指定的buckets个数大于等于列的不同值个数,一般用于数据分布范围比较小的场合,计算结果更精确
等高 equi-height 每个桶需要保存不同值的个数,上下限以及累积频率等。 指定的buckets个数小于列的不同值个数,一般用于数据分布范围比较大的场合

表二:直方图不支持以下场景

场景
加密表、临时表、视图
JSON数据类型、空间(spatial)数据类型
已创建唯一索引的单列
指定USING DATA的时候创建多列直方图

表三:涉及的系统变量

场景 说明
thd->variables.histogram_generation_max_mem_size 默认2000000 这个值越大抽样样本数越多越精确,范围[1000000,max_mem_sz]

表四:抽样率计算

变量 计算公式 说明
row_size_bytes value_map->element_overhead() 所有列每行占用的大小总和,长度=sizeof(value_map_type::value_type) + sizeof(value_map_type::key_type) + 32
rows_in_memory thd->variables.histogram_generation_max_mem_size / row_size_bytes 算出内存可以存放的样本数
sample_percentage min(rows_in_memory / rows_in_table * 100.0, 100.0) 算出需要抽样的百分比,意味着可能不会存所有值

三、实际例子说明

接下来看几个例子来说明上面的代码。

greatsql> CREATE TABLE t4 (d1 INT, d2 int, d3 varchar(100));
greatsql> INSERT INTO t4 VALUES (1,2,'aa1'),(2,1,'bb1'),(2,3,'cc1'),(3,3,'cc1'),(4,2,'ff1'),(4,4,'ert'),(4,2,'f5fg'),(null,2,'ee'),(5,30,'cc1'),(5,4,'fcc1'),(4,10,'cc1'),(6,4,'ccd1'),(null,1,'fee'),(1,2,'aa1'),(2,1,'bb1'),(2,3,'cc1'),(3,3,'cc1'),(4,2,'ff1'),(4,4,'ert'),(4,2,'f5fg'),(null,2,'ee'),(5,30,'cc1'),(5,4,'fcc1'),(4,10,'cc1'),(6,4,'ccd1'),(null,1,'fee'),(1,2,'aa1'),(2,1,'bb1'),(2,3,'cc1'),(3,3,'cc1'),(4,2,'ff1'),(4,4,'ert'),(4,2,'f5fg'),(null,2,'ee'),(5,30,'cc1'),(5,4,'fcc1'),(4,10,'cc1'),(6,4,'ccd1'),(null,1,'fee');查看数据分布情况,发现d1存在数据倾斜。下面的格式即value_map的格式
greatsql> SELECT d1,count(*) FROM t4 GROUP BY d1; 
+------+----------+
| d1   | count(*) |
+------+----------+
|    1 |        3 |
|    2 |        6 |
|    3 |        3 |
|    4 |       12 |
| NULL |        6 |
|    5 |        6 |
|    6 |        3 |
+------+----------+
7 rows in set (0.00 sec)

首先创建自动等高直方图

-- 先创建等高直方图,t4表扣掉null值一共33行,这里创建出来每个桶最多装12行数据。
greatsql> ANALYZE TABLE t4 UPDATE HISTOGRAM ON d1 WITH 3 BUCKETS;
greatsql> SELECT json_pretty(histogram)result FROM information_schema.column_statistics WHERE table_name = 't4';
| {"buckets": [[1,3,0.3076923076923077, 计算公式9/39 ※注意这里的总个数包含null值3],[4,4,0.6153846153846154,1],[5,6,0.8461538461538461,2]],"data-type": "int","null-values": 0.15384615384615385,"collation-id": 8,"last-updated": "2024-10-24 03:15:54.463774","sampling-rate": 1.0,"histogram-type": "equi-height","number-of-buckets-specified": 3
} |-- 先创建等高直方图,t4表扣掉null值一共33行,这里创建出来每个桶最多装9行数据。
greatsql> ANALYZE TABLE t4 UPDATE HISTOGRAM ON d1 WITH 5 BUCKETS;
greatsql> SELECT json_pretty(histogram)result FROM information_schema.column_statistics WHERE table_name = 't4';
| {"buckets": [[1,2,0.23076923076923078,2],[3,3,0.3076923076923077,1],[4,4,0.6153846153846154,1],[5,5,0.7692307692307693,1],[6,6,0.8461538461538461,1]],"data-type": "int","null-values": 0.15384615384615385,"collation-id": 8,"last-updated": "2024-10-24 06:42:40.102386","sampling-rate": 1.0,"histogram-type": "equi-height","number-of-buckets-specified": 5
} |

等高直方图不同桶装的最多数据个数bucket_max_values

桶个数 总行数(扣掉null值) 每个桶最多数据个数
2 33 21
3 33 12
4 33 9
5 33 9

接着创建自动等宽直方图

-- 先创建等高直方图,t4表扣掉null值一共33行,这里创建出来每个桶最多装12行数据。
greatsql> ANALYZE TABLE t4 UPDATE HISTOGRAM ON d1 WITH 6 BUCKETS;
greatsql> select json_pretty(histogram)result from information_schema.column_statistics where table_name = 't4';
| {"buckets": [[1,0.07692307692307693 计算公式3/39 ※注意这里的总个数包含null值],[2,0.23076923076923078],[3,0.3076923076923077],[4,0.6153846153846154],[5,0.7692307692307693],[6,0.8461538461538461]],"data-type": "int","null-values": 0.15384615384615385,"collation-id": 8,"last-updated": "2024-10-24 06:53:37.256033","sampling-rate": 1.0,"histogram-type": "singleton","number-of-buckets-specified": 6
} |
数值 个数 占比
1 3 3/39
2 6 9/39
3 3 12/39
4 12 24/39
5 6 30/39
6 3 33/39

下面看一个sampling-rate小于1的例子

greatsql> CREATE TABLE t5 (d1 INT, d2 int, d3 varchar(100),d4 varchar(100),d5 varchar(100),d6 varchar(100));
greatsql> SET sql_mode=ORACLE;
greatsql> DELIMITER $$
greatsql> CREATE or replace PROCEDURE p1() as
BEGINfor i in 1 .. 1000 loopINSERT INTO t5 VALUES (i,rand()*1000,'aaaaaaaaaa'||i,'bbbb'||i,'cccccc'||i,'ddddddd'||i);end loop;
END;
$$
DELIMITER ;
greatsql> call p1();
-- 把系统变量histogram_generation_max_mem_size设置为最小值
greatsql> SET @@session.histogram_generation_max_mem_size = 1000000;
-- 为了让每个样本更大,这里创建多列直方图
greatsql> ANALYZE TABLE t5 UPDATE HISTOGRAM ON d2,d3,d4,d5,d6 WITH 5 BUCKETS;
greatsql> select json_pretty(histogram)result from information_schema.column_statistics where table_name = 't5';
result: {"buckets": [[1,187,0.2009987515605493,119],[189,373,0.4019975031210986,116],[374,577,0.602996254681648,117],[578,783,0.8039950062421972,112],[784,998,1.0,112]],"data-type": "int","null-values": 0.0,"collation-id": 8,"last-updated": "2024-10-24 07:44:31.520442","sampling-rate": 0.8741258741258742, 这里看到抽样率是87%"histogram-type": "equi-height","number-of-buckets-specified": 5
}

四、总结

从上面直方图创建的步骤我们认识了直方图的类型和创建方法,包括自动和手动两种,以及等宽和等高直方图的区别,学会了查看直方图的桶个数和数据,如果表是一张大表的话,想让样本尽可能多的被抽样,那么系统变量histogram_generation_max_mem_size就设置大一点,这样精确度更高,当然相对的更占硬盘资源。下一节讲直方图的应用。


Enjoy GreatSQL 😃

关于 GreatSQL

GreatSQL是适用于金融级应用的国内自主开源数据库,具备高性能、高可靠、高易用性、高安全等多个核心特性,可以作为MySQL或Percona Server的可选替换,用于线上生产环境,且完全免费并兼容MySQL或Percona Server。

相关链接: GreatSQL社区 Gitee GitHub Bilibili

GreatSQL社区:

社区博客有奖征稿详情:https://greatsql.cn/thread-100-1-1.html

image-20230105161905827

技术交流群:

微信:扫码添加GreatSQL社区助手微信好友,发送验证信息加群

image-20221030163217640

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/883636.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PVE 8.3.1安装后的优化

网上PVE优化文章比较比较多,记录一下优化的过程: 1,删除订阅的弹窗:sed -Ezi.bak "s/(Ext.Msg.show\(\{\s+title: gettext\(No valid sub)/void\(\{ \/\/\1/g" /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && systemctl restart pvepro…

《安富莱嵌入式周报》第350期:Google开源Pebble智能手表,开源模块化机器人平台,开源万用表,支持10GHz HRTIM的单片机,开源CNC控制器

周报汇总地址:http://www.armbbs.cn/forum.php?mod=forumdisplay&fid=12&filter=typeid&typeid=104 视频版: https://www.bilibili.com/video/BV1YPKEeyEeM/目录: 1、Google开源Pebble 智能手表所有代码 2、开源光谱辐射探测系统Pomelo 3、控制器开源 (1)开…

使用Prometheus+Grafana监控MySQL

前提搭建好Prometheus 本次使用Docker搭建 https://www.cnblogs.com/minseo/p/17913003.html搭建好Grafana环境查看 系统环境# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) # uname -a Linux CentOS7K8SHarbor061 3.10.0-1160.102.1.el7.x86_64 #1 SMP Tue…

centos7安装mysql5.6.43报错解决方案!

今天虚拟机安装mysql-community-server-5.6.43版本时出现依赖报错,按照提示的error进行解决! 第一个问题是 Requires: libaio.so.1()(64bit) 解决方案:确保机器联网,使用 yum install -y libaio 第二个问题是缺少perl依赖,可以使用 yum install -y perl* peri-* 安装 这条…

利用apache2本地搭建web环境

下载apache2服务sudo apt install apache2 php -y 启动apache2sudo service apache2 restart命令执行后即可在浏览器访问,访问localhost或127.0.0.1(默认端口为80)即可看到apache的默认页面说明本地站点搭建完毕目录:/var/www/html这个目录下存放的是网站的资源,图片或html…

rancher页面无法显示pod实时日志

异常现象rancher无法通过web页面查看相关pod日志排查步骤1.在k8s的主节点上执行查看日志的命令kubectl get pods -n smartroom-testkubectl logs -f register-85dbdc6df8-nqkc4 -n smartroom-test在k8s主节点上无法查看其它node上运行的pod的运行日志2.在pod运行的主机上查看容…

十. 数据结构

数据结构 二叉树遍历先序遍历:根左右 中序遍历:左根右 后序遍历:左右根 层序遍历:从上到下、从左往右依次遍历通过序列构造二叉树必须有中序序列先序遍历中序遍历后序遍历图的遍历查找哈希表散列表(Hash table, 也叫哈希表), 是根据关键码值(key value)而直接进行访问的数…

微信小程序-引入less

介绍 微信开发者工具默认样式文件是wxss,但该文件格式不支持层级嵌套写法,为了支持此功能可以引入less 或者 sass,微信开发者工具默认不支持less,需要自己安装插件。 Less(Leaner Style Sheets 的缩写)是一种动态样式语言,属于 CSS 预处理器的范畴,它扩展了 CSS 语言,…

unity毛笔笔锋

using System;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI; namespace LastZero.Utility{public class Painting : MonoBehaviour{public Color mColor = Color.black;//画笔颜色//[HideInInspector]public Texture brushTexture;//画笔[HideInI…

.NET 8.0 + Linux 香橙派,实现高效的 IoT 数据采集与控制解决方案

前言 随着物联网(IoT)技术的迅猛发展,智能设备之间的互联互通变得越来越重要。 推荐一套基于 C# 的高效 IoT 系统,该系统运行在 Linux 开发板上,并支持 Modbus RS485 传感器、NPN/PNP 开关等所有符合 485 通讯协议的设备进行数据采集和指令发送。这套系统在为智能家居、智…

DeepSeek 背后的技术:GRPO,基于群组采样的高效大语言模型强化学习训练方法详解

强化学习(Reinforcement Learning, RL)已成为提升大型语言模型(Large Language Models, LLMs)推理能力的重要技术手段,特别是在需要复杂推理的任务中。DeepSeek 团队在 DeepSeek-Math [2] 和 DeepSeek-R1 [3] 模型中的突破性成果,充分展示了强化学习在增强语言模型数学推…