一文读懂MySQL 8.0直方图(2)

简介: 一文读懂MySQL 8.0直方图
  • 无索引时,走直方图
"considered_execution_plans": [
  {
    "table": "`t1`",
    "chosen": true,
    "plan_prefix": [],
    "cost_for_plan": 35080,
    "rows_for_plan": 1,
    "best_access_path": {
      "considered_access_paths": [
        {
          "cost": 35080,   --cost看起来好高
          "chosen": true,  --但还是被选中了
          "access_type": "scan",   --全表扫描
          "rows_to_scan": 347352,  --预计需要扫描行数
          "resulting_rows": 1,   --预计返回行数
          "filtering_effect": [   
            {
              "condition": "(`t1`.`seq` = 1234)",
              "histogram_selectivity": 0.000001   --直方图过滤效果
            }
          ],
          "final_filtering_effect": 0.0000029
        }
      ]
    },
    "condition_filtering_pct": 100
  }

虽然看起来是要走全表扫描,但因为有了直方图,实际上还是很快就能返回结果的。

  • 有索引时,优先走索引
"considered_execution_plans": [

{
"table": "`t1`",
"chosen": true,
"plan_prefix": [],
"cost_for_plan": 0.35,
"rows_for_plan": 1,
"best_access_path": {
"considered_access_paths": [
{
"cost": 0.35, --相当上面直方图的cost,这个就非常低了,所以毫无悬念的选择索引
"rows": 1,
"index": "k1",
"chosen": true,
"access_type": "ref"
},
{
"cause": "heuristic_index_cheaper",
"chosen": false,
"access_type": "range",
"range_details": {
"used_index": "k1"
}
}
]
},
"condition_filtering_pct": 100
}

如果有源码大佬,也请帮忙确认下是不是这样吧。

3. 如何提高直方图的统计精确度

前文我们提到过参数 histogram_generation_max_mem_size,其作用是控制在创建/更新直方图时所需的内存大小。

The maximum amount of memory available for generating 
histogram statistics.

该参数默认值是 20000000(不到20MB),最小值 1000000(约976KB),这是个会话级(session)分配的内存,而且是每次创建/更新直方图都需要分配,执行结束后就释放。

介绍完前置信息,该说重点了,在直方图里如何提高统计精确度。

在扫描InnoDB data page进行直方图数据统计时,大致是这样的步骤:

  1. 估算要统计的列数据类型长度,记为 row_size_bytes。
  2. 可用内存除以每条记录长度,得到预计可以采样的数据量 rows_in_memory = histogram_generation_max_mem_size / row_size_bytes。
  3. 计算得到采样比例 sample_percentage = rows_in_memory / rows_in_table。其中 rows_in_table 是表预估总记录数。
  4. 依照采样比例,扫描data page,得到采样结果。例如,采样比例是10%,那么就是扫描1个page后,跳过9个page,然后继续采样。
    这几个步骤是以我的三脚猫源码阅读水平得到的结果,若有出入,还请留言指正。
    上述步骤所对应的代码是 sql/histograms/histogram.cc,约868行附近的 update_histogram 函数。

MySQL目前对数据长度处理的非常粗粒度,只区分了下面几种情况,这就导致了直方图列实际所需要的内存可能要比它定义的类型长度要更大,也可以看下源码中的定义:

vim sql/histograms/histogram.cc +113

/**
Convert from enum_field_types to Value_map_type.
@param field_type the field type
@param is_unsigned whether the field type is unsigned or not. This is only
considered if the field type is LONGLONG
@return A Value_map_type. May be INVALID if the Value_map does not support
the field type.
*/
static Value_map_type field_type_to_value_map_type(
const enum_field_types field_type, const bool is_unsigned) {
switch (field_type) {
case MYSQL_TYPE_DECIMAL:
case MYSQL_TYPE_NEWDECIMAL:
return Value_map_type::DECIMAL;
case MYSQL_TYPE_TINY:
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_LONG:
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_YEAR:
case MYSQL_TYPE_BIT:
return Value_map_type::INT;
case MYSQL_TYPE_ENUM:
return Value_map_type::ENUM;
case MYSQL_TYPE_SET:
return Value_map_type::SET;
case MYSQL_TYPE_LONGLONG:
return is_unsigned ? Value_map_type::UINT : Value_map_type::INT;
case MYSQL_TYPE_FLOAT:
case MYSQL_TYPE_DOUBLE:
return Value_map_type::DOUBLE;
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_TIME2:
return Value_map_type::TIME;
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_NEWDATE:
return Value_map_type::DATE;
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
case MYSQL_TYPE_TIMESTAMP2:
case MYSQL_TYPE_DATETIME2:
return Value_map_type::DATETIME;
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_VAR_STRING:
case MYSQL_TYPE_STRING:
case MYSQL_TYPE_VARCHAR:
return Value_map_type::STRING;
case MYSQL_TYPE_JSON:
case MYSQL_TYPE_GEOMETRY:
case MYSQL_TYPE_NULL:
default:
return Value_map_type::INVALID;
}

可以看到,把TINYINT(1字节)、SMALLINT(2字节)等都统统按 Value_map_type::INT 来处理了,而这个类型实际上是 longlong 的,也就是 16字节。

另外,每条记录还需要约42字节的额外开销(比大多数据类型长度还要大,尴尬)。

vim sql/histograms/value_map.h +262

/// @return the overhead in bytes for each distinct value stored in the
/// Value_map. The value 32 is obtained from both GCC 8.2 and
/// Clang 8.0 (same as sizeof(value_map_type::node_type) in C++17).
size_t element_overhead() const override {
// TODO: Replace this with sizeof(value_map_type::node_type) when we have
// full C++17 support.
return sizeof(typename value_map_type::value_type) +
sizeof(typename value_map_type::key_type) + 32;
}

其他的几个数据类型也是很粗犷的处理了,在以后的版本应该会改进吧。

如果 histogram_generation_max_mem_size 不够大,则采样比例比较低,就会影响准确度,那么应该设置多少合理呢,可以按照下面这个方法:

  1. 先设置最小值。
  2. 执行一次采样。
  3. 查看采样比例。
  4. 反推出要全部采样所需的内存。
    当然了,如果表数据量特别大,也没必要全部采样,毕竟消耗的内存比较多,而且也需要更多的物理读。
    来个实例演示。
# 设置 histogram_generation_max_mem_size 为最小值
[root@yejr.run]> set session histogram_generation_max_mem_size = 1000000;

# 重置InnoDB Metric计数器
[root@yejr.run]> SET GLOBAL innodb_monitor_enable = 'sampled%';

# 来一发,创建个直方图
[root@yejr.run]> analyze table t1 update histogram on seq;
1 row in set (0.16 sec) --耗时0.16秒

# 查看采样统计比例
[root@yejr.run]> SELECT HISTOGRAM->>'$."sampling-rate"' FROM INFORMATION_SCHEMA.COLUMN_STATISTICS;
+---------------------------------+
| HISTOGRAM->>'$."sampling-rate"' |
+---------------------------------+
| 0.059548422871929935 |
+---------------------------------+

# 查看innodb data page扫描统计
[root@yejr.run]> SELECT NAME, COUNT FROM information_schema.INNODB_METRICS WHERE NAME LIKE 'sampled%';
+-----------------------+-------+
| NAME | COUNT |
+-----------------------+-------+
| sampled_pages_read | 51 | --扫描了51个page
| sampled_pages_skipped | 760 | --跳过了760个page
+-----------------------+-------+

与此同时,我在另一个session中,分别在之前和之后查询上面创建直方图的线程内存消耗情况。

# 第一次查询
[root@yejr.run]> select * from sys.x$memory_by_thread_by_current_bytes where thread_id = 286;
+-----------+-------------------------------+--------------------+-------------------+-------------------+-------------------+-----------------+
| thread_id | user | current_count_used | current_allocated | current_avg_alloc | current_max_alloc | total_allocated |
+-----------+-------------------------------+--------------------+-------------------+-------------------+-------------------+-----------------+
| 286 | thread_pool/tp_one_connection | 163 | 345036 | 2116.7853 | 260280 | 212710814 |
+-----------+-------------------------------+--------------------+-------------------+-------------------+-------------------+-----------------+
1 row in set (0.07 sec)

#第二次查询
[root@yejr.run]> select * from sys.x$memory_by_thread_by_current_bytes where thread_id = 286;
+-----------+-------------------------------+--------------------+-------------------+-------------------+-------------------+-----------------+
| thread_id | user | current_count_used | current_allocated | current_avg_alloc | current_max_alloc | total_allocated |
+-----------+-------------------------------+--------------------+-------------------+-------------------+-------------------+-----------------+
| 286 | thread_pool/tp_one_connection | 165 | 346502 | 2100.0121 | 261712 | 213946226 |
+-----------+-------------------------------+--------------------+-------------------+-------------------+-------------------+-----------------+

两次查询 total_allocated 的差值 1235412(1.17MB) 主要就是由于创建直方图所需的内存。

现在,可以推算所需要的内存大约是

[root@yejr.run]> select 1235412 / 0.059548422871929935;
+--------------------------------+
| 1235412 / 0.059548422871929935 |
+--------------------------------+
| 20746342.8991 |
+--------------------------------+

现在加大内存设置后,再做一次看看

[root@yejr.run]> set session histogram_generation_max_mem_size = 1000000;
[root@yejr.run]> SET GLOBAL innodb_monitor_enable = 'sampled%';
[root@yejr.run]> analyze table t1 update histogram on seq;
1 row in set (3.78 sec) --耗时3.78秒,是上面的23.6倍

[root@yejr.run]> SELECT HISTOGRAM->>'$."sampling-rate"' FROM INFORMATION_SCHEMA.COLUMN_STATISTICS;
+---------------------------------+
| HISTOGRAM->>'$."sampling-rate"' |
+---------------------------------+
| 1.0 |
+---------------------------------+

[root@yejr.run]> SELECT NAME, COUNT FROM information_schema.INNODB_METRICS WHERE NAME LIKE 'sampled%';
+-----------------------+-------+
| NAME | COUNT |
+-----------------------+-------+
| sampled_pages_read | 811 |
| sampled_pages_skipped | 0 | --的确,没有被跳过的page了
+-----------------------+-------+

再次提醒,并不是非得所有page都要被采集到,否则代价可能无法承受。。。

全文完。

            </div>
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
存储 监控 Ubuntu
Linux&Ubuntu安装Minio
Linux&Ubuntu安装Minio
|
9月前
|
人工智能 自然语言处理 搜索推荐
WritingBench:阿里最新大模型写作能力多维测评工具,开源32B深度思考写作模型
近日,阿里研究团队联合中国人民大学和上海交通大学共同开源了WritingBench ——该评估基准覆盖6大领域、100个细分场景,共包含1239条评测数据,以期为生成式写作提供全面的评估。团队进一步发现,凭借思维链技术和动态评估体系的加持,基于Qwen开发的32B创作模型在创意型任务上表现接近顶尖模型R1,为高效能创作开辟了新路径。
883 5
|
数据采集 测试技术 API
python爬虫之app爬取-微信朋友圈
搭建appium环境,appium基本使用,API操作等等
642 0
|
机器学习/深度学习 数据采集 人工智能
运维新纪元:AIOps引领智能运维变革####
本文探讨了人工智能与运维管理深度融合的前沿趋势——AIOps(Artificial Intelligence for Operations),它通过机器学习、大数据分析等技术手段,为现代IT运维体系带来前所未有的智能化升级。不同于传统依赖人力的运维模式,AIOps能够实现故障预测、自动化修复、性能优化等功能,大幅提升系统稳定性和运营效率。文章将深入分析AIOps的核心价值、关键技术组件、实施路径以及面临的挑战,旨在为读者揭示这一新兴领域如何重塑运维行业的未来。 ####
|
运维 监控 大数据
高效运维管理:提升系统稳定性的策略与实践
在当今信息技术飞速发展的时代,运维管理作为保障系统稳定运行的关键环节,其重要性不言而喻。本文将深入探讨如何通过优化运维流程、引入自动化工具和建立完善的监控体系等策略,来有效提升系统的稳定性。同时,结合具体实践案例,分析这些策略在实际工作中的应用效果,为运维人员提供有益的参考和启示。
631 6
|
视频直播 Windows
FFmpeg开发笔记(四十一)结合OBS与MediaMTX实现SRT直播推流
《FFmpeg开发实战》书中介绍了直播中的RTSP、RTMP和SRT协议,SRT提供更低延迟和稳定性。FFmpeg从4.0版起支持SRT,OBS Studio和MediaMTX等工具也已支持。在Windows环境下,通过集成libsrt的FFmpeg,可以建立SRT直播系统。MediaMTX日志显示SRT服务监听8890端口,OBS Studio设置SRT推流至"publish:live"。ffplay和VLC通过"read:live"拉流成功,验证了SRT推拉流功能。更多详情见《FFmpeg开发实战:从零基础到短视频上线》。
854 2
FFmpeg开发笔记(四十一)结合OBS与MediaMTX实现SRT直播推流
|
算法 安全 网络安全
【区块链】深入解析Proof of Work (PoW): 区块链技术的核心驱动力
在区块链技术的宏伟蓝图中,Proof of Work(工作量证明,简称PoW)算法扮演着基石的角色。自比特币白皮书发布以来,PoW已成为确保去中心化网络安全、维护数据完整性的关键机制。本文将深入探讨PoW的工作原理、优势、挑战以及其对区块链生态系统的影响,力求为读者提供一个全面而深入的理解。
650 0
|
Ubuntu Java Linux
Pcap4J抓包
学习使用pacp4j
1159 0
|
机器学习/深度学习 存储 计算机视觉
Elasticsearch 8.X “图搜图”实战
Elasticsearch 8.X “图搜图”实战
|
存储 人工智能 自然语言处理
大模型时代还需要知识图谱么?新一代知识图谱语义框架SPG赋能企业数智化转型
本文以商家经营和风险防控为例,介绍了在企业数字化中的图谱应用。结合当前产业应用和研究进展,本文梳理总结了LLM、KG 在企业数字化中的可能应用。
大模型时代还需要知识图谱么?新一代知识图谱语义框架SPG赋能企业数智化转型