记录MySQL 5.5上,优化器进行cost计算的方法。

第一篇: 单表的cost计算

数据结构:

1. table_share: 包含了表的元数据,其中索引部分:

key_info:一个key的结构体,代表一个索引,包含了:

  1. key_length:key的长度
  2. key_parts:key一共有多少个column
  3. key_part:key中具体的column
  4. rec_per_key:相同的key平均有几条记录

例如:

(gdb) p (table->s->key_info->name)
$16 = 0x8ca0ffbd "PRIMARY"
(gdb) p (table->s->key_info->key_parts)
$17 = 1
(gdb) p (table->s->key_info->rec_per_key)
$18 = (ulong *) 0x8ca0ffe8

2. JOIN:
    mysql_select函数中,创建了 new JOIN(thd, fields, select_options, result)对象,包含了当前查询的所有组件和各种转换结果,其中 :

  1. prepare:进行一些等价交换之类的变化
  2. optimize:选择join的方式和access path
  3. exec:根据执行计划运行查询

3. join_tab:
    包含了一个table访问的cost等一些信息,经过优化后,填充这个结构体

cost的计算方法

cost = cpu cost + io cost

  1. cpu cost:server层对返回的记录数的compare时间
  2. io cost:引擎层根据扫描记录的记录数计算cost

统计信息

这里用到了两部分统计信息:
1. server层的统计信息,保存在table_share中。包括:

  1. key_length
  2. rec_per_key
  3. block_size等

2. innodb层的统计信息,包括:

  1. stat_n_rows
  2. stat_clustered_index_size
  3. stat_sum_of_other_index_size

主要函数调用

make_join_statistics:
--update_ref_and_keys: 添加可以使用的索引。
--get_quick_record_count:
----test_quick_select: 评估每一个join table查询得到的记录数,其中比较不同index的cost, 返回的记录数,选择最优的那个。
choose plan:选择join的顺序

实验过程

CREATE TABLE `xpchild` (
`id` int(11) NOT NULL,
`name` varchar(100) DEFAULT NULL,
`c1` int(11) DEFAULT NULL,
`c2` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `xpchild_name` (`name`),
KEY `xpchild_id_c1` (`id`,`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

实验1: 单值查询

explain select * from xpchild where id =100;

函数调用栈:

make_join_statistics:
    table->quick_condition_rows= table->file->stats.records;
   if (s->type == JT_SYSTEM || s->type == JT_CONST)
   s->found_records=s->records=s->read_time=1; s->worst_seeks=1.0;

结论:单key的查询,join_tab的type=JT_CONST,索引record和read time都是1;最终得到的join->best_read=1.0;

实验2: 范围查询

explain select * from xpchild where id > 100;

step1:update_ref_and_keys: 一共得到两个possible index,

step2:test_quick_select

   

 1. 计算全表扫描的cost:

innodb全表扫描的io cost:

innodb的io cost: s->read_time=(ha_rows) s->table->file->scan_time();
innodb scan time:prebuilt->table->stat_clustered_index_size
等于innodb这张表的聚簇索引的page个数,本身innodb就是聚簇索引表,这里计算的io cost=16
(gdb) p s->read_time
$7 = 16

MySQL server的cpu cost:

scan_time= (double) records / TIME_FOR_COMPARE + 1;
(gdb) p scan_time
$16 = 1359.4000000000001

总的cost:read_time= (double) head->file->scan_time() + scan_time + 1.1;

(gdb) p read_time
$18 = 1376.5

继续函数栈:

根据possible index,生成sel_tree;
get_best_group_min_max: 这里没有使用到
get_key_scans_params:根据sel_tree找到更好的cost

2. 计算full index的cost

find_shortest_key: 在覆盖索引中选择length最短的那个。
      get_index_only_read_time:这里如果有覆盖索引(covering index)那么就会计算此覆盖索引的cost。
      full index scan的计算方法:

uint keys_per_block= (param->table->file->stats.block_size/2/
(param->table->key_info[keynr].key_length+
param->table->file->ref_length) + 1);
read_time=((double) (records+keys_per_block-1)/(double) keys_per_block);

这里假设:一个块中,只使用了一半的空间写入数据,
     如果计算的key_read_time > read_time, 则read_time= key_read_time,从此不再使用全表扫描。

3. pk 索引计算的cost

进入get_key_scans_params函数:选择比传入的read_time小的cost的执行计划,生成一个TRP_RANGE对象返回。

step1: 评估范围扫描的记录数(check_quick_select)

check_quick_keys:根据key,min,max值来评估记录数,并把records记录到table->quick_rows[key]中,以便后续需要。

(gdb) p *min_key
$82 = 100 'd'

ha_innobase::records_in_range: innodb引擎根据min和max值来评估记录数。
    计算方法:innodb对b_tree中范围确定的page的个数和record_per_page进行计算,当评估>all_record/2时,就取all_record/2。

(gdb) p records
$112 = 3396
step 2: 计算cost

根据pk range估算的records=3396,调整table->quick_condition_rows从全表的6792到现在的3396。
计算cost:

cpu_cost= (double) found_records / TIME_FOR_COMPARE;
cpu_cost= 679.20000000000005
io_cost = param->table->file->read_time(keynr,param->range_count,found_records);

found_read_time = cpu_cost + io_cost + 0.01
found_read_time = 683.74750000000006

这样,通过pk扫描的cost远小于前面第一阶段的全表扫描的代价。

4. 计算普通索引的cost

因为都可以使用前导列进行查询,查询的效率的差别在于非主键索引需要回到聚簇索引中查询非索引列。
所以,innodb返回的found_rows=6556(包含了扫描索引xpchild_id_c1 和primary key的), 所以最终计算的cost大于使用pk的cost。

最终计算得到的cost=7868.21 远高于pk索引的cost。

结论: 最终选择了pk的索引进行range扫描

下一篇实验待续:关联查询。

MySQL优化器cost计算的更多相关文章

  1. 0104探究MySQL优化器对索引和JOIN顺序的选择

    转自http://www.jb51.net/article/67007.htm,感谢博主 本文通过一个案例来看看MySQL优化器如何选择索引和JOIN顺序.表结构和数据准备参考本文最后部分" ...

  2. 机智的MySQL优化器 --- is null

    [介绍] 工作的越久越到的的问题越多,就越是觉得一些“老话”历久弥新:由于最近的学习计划是深入的学习一遍MySQL优化器:学习过程中的一些成果 也会发布到这里,一来是为了整理自己已经知道的和新学到的, ...

  3. MySQL优化器功能开关optimizer_switch

    MySQL 8.0新增特性 use_invisible_indexes:是否使用不可见索引,MySQL 8.0新增可以创建invisible索引,这一开关控制优化器是否使用invisible索引,on ...

  4. 数据库 mysql 优化器原理

    MySQL查询优化器有几个目标,但是其中最主要的目标是尽可能地使用索引,并且使用最严格的索引来消除尽可能多的数据行. 你的最终目标是提交SELECT语句查找数据行,而不是排除数据行.优化器试图排除数据 ...

  5. MySQL优化器 --- index_merge

    [背景] 对于关系数据库中的一张表,通常来说数据页面的总大小要比较某一个索引占用的页面要大的多(上面说的索引是不包涵主键索引的); 更进一步我们可以推导出,如果我们通过读索引就能解决问题,那么它相比读 ...

  6. 《Mysql - 优化器是如何选择索引的?》

    一:概念 - 在 索引建立之后,一条语句可能会命中多个索引,这时,索引的选择,就会交由 优化器 来选择合适的索引. - 优化器选择索引的目的,是找到一个最优的执行方案,并用最小的代价去执行语句. 二: ...

  7. 如何干涉MySQL优化器使用hash join

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. GreatSQL是MySQL的国产分支版本,使用上与MySQL一致. 前言 实验 总结 前言 数据库的优化器相当于人类的大 ...

  8. MySQL优化器不使用索引的情况

    优化器选择不适用索引的情况 有时候,有乎其并没有选择索引而去查找数据,而是通过扫描聚集索引,也就是直接进行全表的扫描来得到数据.这种情况多发生于范围查找.JOIN链接操作等情况.例如 ; 通过SHOW ...

  9. MySQL优化器join顺序

    前一篇介绍了cost的计算方法,下面测试一下两表关联的查询: 测试用例 CREATE TABLE `xpchild` ( `id` int(11) NOT NULL, `name` varchar(1 ...

随机推荐

  1. MVC中如何在controller的action中输出JS到页面上

    MVC中如何在controller的action中输出JS到页面上 可以通过Http上下文对象(httpContext)就可以了,在Action中的HttpContext就是这个Action所指向的页 ...

  2. c语言学习之基础知识点介绍(十三):枚举的介绍和使用

    一.枚举的介绍 /* 枚举:限制的待选项. 语法: enum 枚举名{ 选项1, 选项2, 选项3, ........ 选项n }; 注意:枚举中,选项之间用 , 隔开,最后一个不用加 , :并且枚举 ...

  3. 03_MySQL中文乱码处理_01_MySQl数据库字符集知识

    [MySql数据库常见字符集介绍] 在互联网环境中,使用MySql时常用的字符集有: [如何选择合适的字符集] 1.如果处理各种各样的文字,发布到不同语言的国家地区,应选Unicode字符集,对MyS ...

  4. (hdu)1285 确定比赛名次

    Problem Description 有N个比赛队(<=N<=),编号依次为1,,,....,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接 ...

  5. LINQ to Entities 查询注意事项

    1> 排序信息丢失 如果在排序操作之后执行了任何其他操作,则不能保证这些附加操作中会保留排序结果.这些操作包括 Select 和 Where 等.另外,采用表达式作为输入参数的 First 和 ...

  6. erlang 里的if 和 case

    case Expression of Pattern1 [when Guard1] -> Expr_seq1; Pattern2 [when Guard2] -> Expr_seq2; … ...

  7. linux文件属性权限相关

    一个linux目录或者文件,都会有一个所属主和所属组. 所属主,即文件的拥有者,而所属组,即该文件所属主所在的一个组. linux文件属性 包括文件类型 - d  l  b c s 依次表示 普通文件 ...

  8. spring的基本配置

    一:web.xml (1)spring mvc的配置 <servlet> <description>spring mvc servlet</description> ...

  9. 3DMAX 建立场景 工作流程

    建立3D渲染首先建立房型.毕竟我们在做的是三维房间的渲染.建立房型线有几个环节都要用到 我们看一眼最终的渲染效果. 利用我们第一步建立的房型线做模型 房型线通过膨胀变成墙壁 再通过房型线生成屋顶天花和 ...

  10. 如何使用KMS激活win10和office

    首先你需要下载一个kms软件,地址: https://yunpan.cn/cRxVNy2LRXjBt (提取码:d5d8) 然后搭建kms服务器,很简单.启动软件,选择“附加”Tab, 点连接到服务器 ...