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物理优化器代价模型分析【原创】的更多相关文章

  1. [源码解析] PyTorch分布式优化器(3)---- 模型并行

    [源码解析] PyTorch分布式优化器(3)---- 模型并行 目录 [源码解析] PyTorch分布式优化器(3)---- 模型并行 0x00 摘要 0x01 前文回顾 0x02 单机模型 2.1 ...

  2. MySQL追踪优化器小试

    首先看一下MySQL追踪优化器的典型用法: 打开:SET optimizer_trace="enabled=on"; 查询优化器的信息:SELECT * FROM INFORMAT ...

  3. mysql之优化器、执行计划、简单优化

    mysql之优化器.执行计划.简单优化 2018-12-12 15:11 烟雨楼人 阅读(794) 评论(0) 编辑 收藏 引用连接: https://blog.csdn.net/DrDanger/a ...

  4. keras channels_last、preprocess_input、全连接层Dense、SGD优化器、模型及编译

    channels_last 和 channels_first keras中 channels_last 和 channels_first 用来设定数据的维度顺序(image_data_format). ...

  5. MySQL 性能优化神器 Explain 使用分析

    简介 MySQL 提供了一个 EXPLAIN 命令, 它可以对 SELECT 语句进行分析, 并输出 SELECT 执行的详细信息, 以供开发人员针对性优化. EXPLAIN 命令用法十分简单, 在 ...

  6. MySQL Optimization 优化原理

    MySQL Optimization 优化原理 MySQL逻辑架构 如果能在头脑中构建一幅MySQL各组件之间如何协同工作的架构图,有助于深入理解MySQL服务器.下图展示了MySQL的逻辑架构图. ...

  7. 详解MYSQL各种优化原理

    说起MySQL的查询优化,相信大家收藏了一堆奇技淫巧:不能使用SELECT *.不使用NULL字段.合理创建索引.为字段选择合适的数据类型..... 你是否真的理解这些优化技巧?是否理解其背后的工作原 ...

  8. mysql数据库优化(转)

    今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我们程序员需要去关注的事情.当我们去设计数据库表结构,对操作数据 ...

  9. MySQL 高性能优化实战总结

    1 前言 2 优化的哲学 3 优化思路 3.1 优化什么 3.2 优化的范围有哪些 3.3 优化维度 4 优化工具有啥? 4.1 数据库层面 4.2 数据库层面问题解决思路 4.3 系统层面 4.4 ...

随机推荐

  1. ctf常见源码泄露

    前言 在ctf中发现很多源码泄露的题,总结一下,对于网站的搭建要注意删除备份文件,和一些工具的使用如git,svn等等的规范使用,避免备份文件出现在公网 SVN源码泄露 原理 SVN(subversi ...

  2. akka-streams - 从应用角度学习:basic stream parts

    实际上很早就写了一系列关于akka-streams的博客.但那个时候纯粹是为了了解akka而去学习的,主要是从了解akka-streams的原理为出发点.因为akka-streams是akka系列工具 ...

  3. 杭电oj2093题,Java版

    杭电2093题,Java版 虽然不难但很麻烦. import java.util.ArrayList; import java.util.Collections; import java.util.L ...

  4. Springboot中WebMvcConfigurer接口详解

    Springboot 使用越来越多,企业的基本框架,到Springcloud分布式,可以说无论面试还是平常技术学习,一说到spring几乎就就代替了Java,可以说spring,springboot的 ...

  5. memcached缓存:安装和清缓存

    一.安装步骤: 1.将工具一直解压,解压到最底层的exe目录

  6. Unit1:Android

    unit1 1.安卓版本 最新数据访问维基百科 2008年,android1.0 2011年,android3.0,平板失败 同年10月,android4.0,无差别使用 2014年,android5 ...

  7. [LeetCode]678. 有效的括号字符串、20. 有效的括号(栈)

    题目 678. 有效的括号字符串 给定一个只包含三种字符的字符串:( ,) 和 *,写一个函数来检验这个字符串是否为有效字符串.有效字符串具有如下规则: 任何左括号 ( 必须有相应的右括号 ). 任何 ...

  8. 安装Scrapy提示ERROR: 'xslt-config' 不是内部或外部命令,也不是可运行的程序

    环境:win7.Python 2.7.12.PyCharm 2016.3 Requirement already satisfied (use --upgrade to upgrade): scrap ...

  9. Linux实战(16):Centos history命令进阶

    因在使用history命令是发现历史命令不同步,没有时间戳,在查看历史操作的时候很不方便,特查询了相关文档,发现可通过系统自带的logger来记录跟踪,历史命令执行的用户,时间,登录IP. vim / ...

  10. matlab数字图像处理-给图片加入可视水印信息

    将文件夹路径改成了我想要存放代码的文件夹下 然后也是在网上百度,找到了一个代码,敲着模仿了一个 自己建立了一个脚本文件 >>edit test1 然后在脚本中添加 %读取待嵌入水印的图像和 ...