mysql物理优化器代价模型分析【原创】
1. 引言
mysql的sql server在根据where condition检索数据的时候,一般会有多种数据检索的方法,其会根据各种数据检索方法代价的大小,选择代价最小的那个数据检索方法。
比如说这个语句,where col1=x and col2=y and col3 >z , 同时存在inx_col1,inx_col2,inx_col3,inx_col1_col2_col3这四个索引,sql server要解决的问题有1)选择哪个索引、 2)是索引range扫描还是ref扫描、3)table scan的方式是否可行。
mysql会根据以下几种数据检索策略选择代价最小的策略来从数据表中获取数据,1)各个索引的range scan代价 2)各个索引的ref scan代价 3)table scan的代价。如何计算这些代价,是本文详细说明的重点。
总代价cost = cpu cost + io cost。
2 . 代价因子
mysql的代价因子在内存中有一份副本,由Server_cost_constants 和SE_cost_constants两个类组成。这两个类的具体数据成员如下。
Mysql Server 代价因子
Server_cost_constants {
m_row_evaluate_cost //行记录条件谓词评估代价
m_key_compare_cost //键值比较代价
m_memory_temptable_create_cost //内存临时表创建代价
m_memory_temptable_row_cost //内存临时表的行代价
m_disk_temptable_create_cost //磁盘临时表创建代价
m_disk_temptable_row_cost
}
存储引擎代价因子
SE_cost_constants{
m_memory_block_read_cost //从buffer pool中读取一个页面的代价
m_io_block_read_cost //从文件系统中读取一个页面的代价,buffer miss的场景
m_memory_block_read_cost_default
m_io_block_read_cost_default
}
mysql的代价因子在系统的持久化系统表中也有一份副本,对应mysql.server_cost 和 mysql.engine_cost两个表,这两个表中的字段与 内存中的类字段相同。DBA可以根据实际的硬件情况测试,测试出最适合的代价因子,然后update系统表中对应的字段。再然后执行flush OPTIMIZER_COSTS命令,将修改反应到内存中数据,这样新连接上来的mysql session会读取到内存中数据,然后以新的代价因子计算代价数。
代价因子如何根据实际的硬件环境与负载压力自适应地调整,是一个重要的研究课题。
3 . 统计信息
sql server需要的统计信息是由存储引擎innodb提供的,调用innodb提供的api可以获取这些统计信息,本文的后半部分会罗列这些api。innodb的统计信息根据需要可以持久化到系统表中。mysql.innodb_table_stats和mysql.innodb_index_stats存储了表的统计信息和索引的统计信息。
mysql.innodb_table_stats表中字段说明
database_name 库名
table_name 表名
n_rows 表中的数据行数
clustered_index_size 聚集索引的页面数
sum_of_other_index_sizes 其他非主键索引的页面数
last_update 最后更新这张表的时间
mysql.innodb_index_stats 表中字段说明
database_name 库名
table_name 表名
index_name 索引名
stat_name 统计项名称
stat_value 统计项值
sample_size 采样的页面数
last_update 最后更新这张表的时间 其中stat_name 统计项名称包括:
n_diff_pfxNN 为不同前缀列的cardinality,即不同前缀字段的 distinct value个数
n_leaf_page 索引叶子节点页面数目
size 索引页面数目
4. 代价的计算公式
cpu代价计算
double row_evaluate_cost(double rows)
{
return rows * m_server_cost_constants->row_evaluate_cost();
}
table scan IO代价计算
Cost_estimate handler::table_scan_cost()
{
double io_cost= scan_time() * table->cost_model()->page_read_cost(1.0);
}
ref and range scan IO代价计算
聚集索引扫描IO代价计算公式
Cost_estimate handler::read_cost(uint index, double ranges, double rows)
{
double io_cost= read_time(index, static_cast<uint>(ranges),
static_cast<ha_rows>(rows)) *
table->cost_model()->page_read_cost(1.0);
}
二级索引覆盖扫描(不需要回表)IO代价计算公式
Cost_estimate handler::index_scan_cost(uint index, double ranges, double rows)
{
double io_cost= index_only_read_time(index, rows) *
table->cost_model()->page_read_cost_index(index, 1.0);
}
二级索引非覆盖扫描(需要回表)IO代价计算公式
min( table→cost_model()→page_read_cost(tmp_fanout), tab→worst_seeks )
估算读取 pages个聚集索引页面所花费的代价, page数乘以代价因子
double Cost_model_table::page_read_cost(double pages)
估算读取 pages个指定 index索引页面所花费的代价数。
double Cost_model_table::page_read_cost_index(uint index, double pages)
5. innodb统计信息api
全表扫描聚集索引时,聚集索引(主键)占用的所有页面数
double ha_innobase::scan_time()
估算在聚集索引上,扫描 rows 条记录,需要读取的页面数
double ha_innobase::read_time(uint index, double ranges, double rows)
估算在指定 keynr索引进行覆盖扫描(不需要回表),扫描 records条记录,需要读取的索引页面数
double handler::index_only_read_time(uint keynr, double records)
估算指定 keynr索引在范围(min_key,max_key)中的记录数量
ha_innobase::records_in_range(
uint keynr, /*!< in: index number */
key_range *min_key, /*!< in: start key value of the
key_range *max_key) /*!< in: range end key val, may
)
估算聚集索引内存中页面数占其所有页面数的比率
double handler::table_in_memory_estimate()
估算二级索引内存中页面数占其所有页面数的比率
double handler::index_in_memory_estimate(uint keyno)
6.开启优化器跟踪
set session optimizer_trace="enabled=on"; explain your sql select * from information_schema.optimizer_trace;
7.优化器跟踪示例


"rows_estimation": [
{
"table": "`tab`",
"range_analysis": {
"table_scan": {
"rows": 5,
"cost": 4.1
},
"potential_range_indexes": [
{
"index": "PRIMARY",
"usable": false,
"cause": "not_applicable"
},
{
"index": "inx_clo2",
"usable": true,
"key_parts": [
"clo2",
"clo1"
]
},
{
"index": "inx_clo3",
"usable": true,
"key_parts": [
"clo3",
"clo1"
]
},
{
"index": "inx_clo2_clo3",
"usable": true,
"key_parts": [
"clo2",
"clo3",
"clo1"
]
}
],
"best_covering_index_scan": {
"index": "inx_clo2_clo3",
"cost": 2.0606,
"chosen": true
},
"setup_range_conditions": [
],
"group_index_range": {
"chosen": false,
"cause": "not_group_by_or_distinct"
},
"analyzing_range_alternatives": {
"range_scan_alternatives": [
{
"index": "inx_clo2",
"ranges": [
"hu <= clo2 <= hu"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": true,
"using_mrr": false,
"index_only": false,
"rows": 2,
"cost": 3.41,
"chosen": false,
"cause": "cost"
},
{
"index": "inx_clo3",
"ranges": [
"huan <= clo3 <= huan"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": true,
"using_mrr": false,
"index_only": false,
"rows": 1,
"cost": 2.21,
"chosen": false,
"cause": "cost"
},
{
"index": "inx_clo2_clo3",
"ranges": [
"hu <= clo2 <= hu AND huan <= clo3 <= huan"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": true,
"using_mrr": false,
"index_only": true,
"rows": 1,
"cost": 1.21,
"chosen": true
}
],
"analyzing_roworder_intersect": {
"intersecting_indexes": [
{
"index": "inx_clo2_clo3",
"index_scan_cost": 1,
"cumulated_index_scan_cost": 1,
"disk_sweep_cost": 0,
"cumulated_total_cost": 1,
"usable": true,
"matching_rows_now": 1,
"isect_covering_with_this_index": true,
"chosen": true
}
],
"clustered_pk": {
"clustered_pk_added_to_intersect": false,
"cause": "no_clustered_pk_index"
},
"chosen": false,
"cause": "too_few_indexes_to_merge"
}
},
"chosen_range_access_summary": {
"range_access_plan": {
"type": "range_scan",
"index": "inx_clo2_clo3",
"rows": 1,
"ranges": [
"hu <= clo2 <= hu AND huan <= clo3 <= huan"
]
},
"rows_for_plan": 1,
"cost_for_plan": 1.21,
"chosen": true
}
}
}
]
},
{
"considered_execution_plans": [
{
"plan_prefix": [
],
"table": "`tab`",
"best_access_path": {
"considered_access_paths": [
{
"access_type": "ref",
"index": "inx_clo2",
"rows": 2,
"cost": 2.4,
"chosen": true
},
{
"access_type": "ref",
"index": "inx_clo3",
"rows": 1,
"cost": 1.2,
"chosen": true
},
{
"access_type": "ref",
"index": "inx_clo2_clo3",
"rows": 1,
"cost": 1.2,
"chosen": false
},
{
"rows_to_scan": 1,
"access_type": "range",
"range_details": {
"used_index": "inx_clo2_clo3"
},
"resulting_rows": 1,
"cost": 1.41,
"chosen": false
}
]
},
"condition_filtering_pct": 40,
"rows_for_plan": 0.4,
"cost_for_plan": 1.2,
"chosen": true
}
]
},
{
"attaching_conditions_to_tables": {
"original_condition": "((`tab`.`clo2` = 'hu') and (`tab`.`clo3` = 'huan'))",
"attached_conditions_computation": [
],
"attached_conditions_summary": [
{
"table": "`tab`",
"attached": "(`tab`.`clo2` = 'hu')"
}
]
}
},
{
"refine_plan": [
{
"table": "`tab`"
}
]
}
]
mysql物理优化器代价模型分析【原创】的更多相关文章
- [源码解析] PyTorch分布式优化器(3)---- 模型并行
[源码解析] PyTorch分布式优化器(3)---- 模型并行 目录 [源码解析] PyTorch分布式优化器(3)---- 模型并行 0x00 摘要 0x01 前文回顾 0x02 单机模型 2.1 ...
- MySQL追踪优化器小试
首先看一下MySQL追踪优化器的典型用法: 打开:SET optimizer_trace="enabled=on"; 查询优化器的信息:SELECT * FROM INFORMAT ...
- mysql之优化器、执行计划、简单优化
mysql之优化器.执行计划.简单优化 2018-12-12 15:11 烟雨楼人 阅读(794) 评论(0) 编辑 收藏 引用连接: https://blog.csdn.net/DrDanger/a ...
- keras channels_last、preprocess_input、全连接层Dense、SGD优化器、模型及编译
channels_last 和 channels_first keras中 channels_last 和 channels_first 用来设定数据的维度顺序(image_data_format). ...
- MySQL 性能优化神器 Explain 使用分析
简介 MySQL 提供了一个 EXPLAIN 命令, 它可以对 SELECT 语句进行分析, 并输出 SELECT 执行的详细信息, 以供开发人员针对性优化. EXPLAIN 命令用法十分简单, 在 ...
- MySQL Optimization 优化原理
MySQL Optimization 优化原理 MySQL逻辑架构 如果能在头脑中构建一幅MySQL各组件之间如何协同工作的架构图,有助于深入理解MySQL服务器.下图展示了MySQL的逻辑架构图. ...
- 详解MYSQL各种优化原理
说起MySQL的查询优化,相信大家收藏了一堆奇技淫巧:不能使用SELECT *.不使用NULL字段.合理创建索引.为字段选择合适的数据类型..... 你是否真的理解这些优化技巧?是否理解其背后的工作原 ...
- mysql数据库优化(转)
今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我们程序员需要去关注的事情.当我们去设计数据库表结构,对操作数据 ...
- MySQL 高性能优化实战总结
1 前言 2 优化的哲学 3 优化思路 3.1 优化什么 3.2 优化的范围有哪些 3.3 优化维度 4 优化工具有啥? 4.1 数据库层面 4.2 数据库层面问题解决思路 4.3 系统层面 4.4 ...
随机推荐
- MySQL互联网业务使用建议
一.基础规范 表存储引擎必须使用InnoDB 表字符集默认使用utf8,必要时候使用utf8mb4 解读: (1)通用,无乱码风险,汉字3字节,英文1字节 (2)utf8mb4是utf8的超集,有存储 ...
- shell 逻辑判断
|| && 查了 Stack Overflow ||,表示或,从前往后执行,执行第一个真 &&,前者是真才会接着执行 ; 表示接着往下执行,不管前者如何 #!/bin/ ...
- pytest文档1-pytest+Allure+jenkins+邮箱发送
前言: 1.pytest+allure是目前很多公司使用较多的一种报告样式,因为它更详细,各种指标更直观(简单的说就是看着更高大上,更能装X). 环境准备: 1.Windows10 2.Allure ...
- 【二叉树-最长路径系列(任意路径)】直径、最长同值路径、 最大路径和(DFS、树形DP)
总述 这类题目都是求一个最长路径,这个路径可以不经过根节点. 使用dfs(即递归地遍历树)的方法.维护一个全局最长路径max作为最终结果,而递归方法dfs返回的是含根节点的最长路径.(若不使用全局变量 ...
- HBase表的数据导出和导入
1. 表数据导出 hbase org.apache.hadoop.hbase.mapreduce.Export test file:///home/hadoop/test (导入到本地) hbase ...
- 基于JavaScript的表格设计:按序添加或删除班级的学生信息
目的: 制作一个表格,显示班级的学生信息 功能: 鼠标移到不同行,背景色发生改变,离开恢复原背景色 添加.删除按钮,可添加,可删除. 程序流程: 首先先建立绑定事件函数. 其次建立鼠标移动改变背景色函 ...
- python中库引用与import
在蟒蛇绘制函数中,多有turtle. ,称它为<a>.<b>的编码风格 库引用 扩充python程序功能的方式 使用import保留字完成,采用<a>.< ...
- 数组如何在ElasticSearch中索引
一.简介 在ElasticSearch里没有专门的数组类型,任何一个字段都可以有零个和多个值.当字段值的个数大于1时,字段类型就变成了数组. 下面以视频数据为例,介绍ElasticSearch如何索引 ...
- dpwwn-01靶机渗透
dpwwn-01 主机发现+端口扫描 发现3306端口,80端口. 访问80端口,进行目录爆破,没有什么发现,主要点还是在数据库. 登录,没有密码. 进入数据库,查找敏感信息. 查看到ssh的用户,进 ...
- [BJDCTF 2nd]文件探测
[BJDCTF 2nd]文件探测 这两天刷题碰到了一道稍微有难度的题,记录一下,有一些点是未被掌握到的. home.php: <?php setcookie("y1ng", ...