当前位置: 首页 > news >正文

MySQL 是怎样运行的:单表访问方法及基于成本的优化

1. 单表访问方法

访问方法:执行查询的方式

只需于选取代价最小的那种访问方式去执行单表查询语句即可。

1.1 const

主键值或者唯一的二级索引定位一条记录(与常数等值比较时才有效)

需要注意的是,使用唯一二级索引查询列为 NULL 值时不会使用 const 访问方法来执行,使用 ref 访问方式。

key IS NULL

1.2 ref

  1. 普通二级索引与常数进行等值比较。

  2. 二级索引允许存储 NULL 值时,执行包含 key IS NULL

  3. 二级索引中包含多个列时,只有最左边连续的列与常数进行等值比较,就可以使用 ref 访问方法

1.3 ref_or_null

二级索引等于某个常数,会有列值为 NULL 的记录,即
key1 = 'abc' OR key1 IS NULL
值为 NULL 的记录会被放在索引最左边。

1.4 range

使用索引查询时,扫描区间为若干个单点扫描区间(IN)或者范围扫描区间。

注意:不包含仅一个单点扫描区间,并且这里的扫描区间不能为 ( − ∞ , + ∞ ) (-\infty,+\infty) (,+)

1.5 index

扫描全部二级索引记录的访问方法(不用执行回表),即需要查询的数据及搜索条件均在索引列中

特例:人为规定;通过全表扫描执行查询,添加了 ORDER BY 主键

1.6 all

全表扫描,直接扫描全部的聚簇索引记录

1.7 索引合并

一般情况下只会为单个索引生成扫描空间,但也可能为多个索引生成扫描空间。

这里的合并是,先根据二级索引得到二级索引记录,先不进行回表,等合并后,再进行回表操作。

(1)Intersection 索引合并

要求从每个索引中获取到的二级索引记录都是按照主键值排序的。

带来的好处是:两个有序数组求交/并集或去重比较容易;按主键排序后进行回表操作容易形成顺序 IO。

这对二级索引来说,相当于要求等值查询,而非范围查询,并且需要给定所有的索引列。

例如WHERE key1 = 'a' AND id > 9000,对聚簇索引,本身就是按照主键顺序排序的,可以是范围查询,因为二级索引是包含索引列和主键的,在索引列值相同的情况下,是按主键排序的,只需扫描二级索引即可,并不需要扫描聚簇索引。

(2)Union 索引合并

要求从每个索引中获取到的二级索引记录都是按照主键值排序的。

例如WHERE key1 = 'a' OR id > 9000,此时需要扫描聚簇索引

可以组合 Intersection 与 Union 索引合并

(3)Sort-Union 索引合并

针对单独根据搜索条件从某个二级索引中获取记录比较少的情况。

很多情况下无法保证二级索引记录按照主键排序,例如范围扫描,此时要进行额外的排序工作。先获取二级索引记录,按照主键值进行排序后再进行合并。

2. 连接

涉及单表的条件

内外连接的根本区别为:在驱动表中不符合 ON 子句中的连接条件时,是否会加入到最后的结果集中。

(1)每获得一条驱动表(外循环中的表)记录,就立即到被驱动表(内循环中的表)中寻找匹配的记录。

对驱动表而言,会使用只涉及驱动表的条件来进行单表访问。

WHERE 子句中的过滤条件,不论是内连接还是外连接,不符合的记录都不会被加入到最后的结果集。

ON 子句中的过滤条件,专门针对外连接,驱动表中的记录都会被加入到最后的结果集。

对外连接而言,必须使用 ON 子句来指出连接条件。

(2)左(外)连接,LEFT (OUTER) JOIN,左侧的为驱动表;

右(外)连接,RIGHT (OUTER) JOIN,右侧的为驱动表;

内连接,推荐书写为 INNER JOIN

2.1 连接算法

基于块的嵌套循环连接,涉及到 Join Buffer。先把驱动表结果集放在Join Buffer 中,每一条被驱动表记录可以一次性匹配多条驱动表结果集中的记录;需要注意的是: Join Buffer 中不会存放驱动表记录的所有列,只有查询列表中列和过滤条件中的列。

3. 基于成本的优化

MySQL 的执行成本:IO 成本和 CPU 成本。

读取一个页面花费成本为 1.0;
读取及检测一条记录是否符合搜索条件的成本默认为 0.2;

3.1 优化步骤

(1)找出可能使用的索引

索引列与常数使用=、<=>、IN、NOT IN、IS NULL、IS NOT NULL、>、<、>=、<=、BETWEEN、!=(<>)、LIKE 进行前缀匹配时

(2)计算全表扫描的代价(通过查看表的一系列统计信息)

SHOW TABLE STATUS LIKE '表名';

ROW:表中的记录数,对 MyISAM 表来说,该值是准确的。对 InnoDB 来说,是估计值。
Data_length;表占用的字节数。对 MyISAM 表来说,该值是数据文件的大小;对 InnoDB 来说,是聚簇索引占用的存储空间大小。

(3)计算使用不同索引执行查询的代价

MySQL 会先分析使用唯一二级索引的成本,再分析使用普通索引的成本。

对使用二级索引 + 回表的方式,成本主要为:

  1. 扫描区间的数量:认为一个扫描区间的 IO 成本与读取一个页面相同。

  2. 需要回表的记录数:会在 B+ 数中找到区间左右端点记录,统计它们之间的数量。(即使对单点扫描区间也会这样做)
    当相隔不超过 10 个页面时,会根据每页中 Page Header 的 PAGE_N_RECS进行累加。相距太远,也会沿着最左记录向右读 10 个页面,计算平均数量,再乘相隔页面数。

  3. 每次回表操作都相当于访问一个页面。根据上述需要回表的记录数。

  4. 回表操作完成后,再检测其他搜索条件。根据上述需要回表的记录数。

(4)注意事项

在计算二级索引成本的第 2 步时,会少量访问 B+ 数中的数据,这种计算方式称为 index dive。

单点扫描区间大于等于eq_range_index_dive_limit时,不会使用 index dive 的计算方式,使用索引统计数据进行估算。

SHOW INDEX FROM 索引名;主要使用 Cardinality 信息。
单个值的重复次数 Rows / Cardinality ,单个值的重复次数 * 单点扫描区间数量 = 需要回表的记录数

3.2 Join 成本

单次查询驱动表的成本;查询得到的记录条数称为扇出(fanout);

扇出值 × 查询被驱动表的成本

对内连接来说,可以选择左侧或右侧是驱动表;

对被驱动表而言,确定好驱动表后连接条件可以确定,例如s1.key1 = 常数,但在优化过程中是无法得知具体的常数值,那么如何进行估算?这里也是采用统计数据中的单个值的重复次数。

多表 Join,需要考虑连接顺序,采用了一些提前结束某种连接顺序评估等启发式规则。

4. InnoDB 统计数据

innodb_stats_persistent永久性/非永久性地存储统计数据,默认为ON

InnoDB 默认以表为单位来收集和存储统计数据,在创建表时可以指定该表统计数据的存储方式。

… Engine=InnoDB, STATS_PERSISTENT = (1 | 0); 存储到 磁盘 | 内存,默认与innodb_stats_persistent相同
… Engine=InnoDB, STATS_SAMPLE_PAGES = 数量;n_rows统计时的采样数量,默认与innodb_stats_persistent_sample_pages相同

4.1 基于磁盘的永久性统计数据

实际上是把统计数据存储到两个表中:innodb_table_stats 和 innodb_index_stats;

(1)innodb_table_stats ;每条记录对应一个表的统计数据;

主键为(database_name,table_name)

  1. n_rows 项的收集
    从聚簇索引中选取几个叶子节点页面,计算平均包含的记录数量。
  2. clustered_index_size 和 sum_of_other_index_sizes 的收集
    从数据字典中找出索引对应根页面位置。
    从根页面读取对应的 Segment Header,找到对应段信息存储的位置。
    获取叶子节点段和非叶子节点段的 INODE Entry。
    统计段中索引零散页面,及 FREE、NOT_FULL、FULL 这 3 个链表的 List Length 字段占用区的数量。(这里简单的都计算上了,实际上可能某些区中有些页面仍是空闲的,即包括了分配给了段但尚未使用的页面)

(2)innodb_index_stats ;每条记录对应一个索引的统计数据;

该表的主键为(database_name,table_name,index_name,stat_name)

stat_name 是统计项的名称。

n_leaf_pages:该索引叶子节点占用的页面数
size:该索引占用页面数
n_diff_pfxNN:对应索引列不重复的值。例如 01表示的是 key_part1 这一列不重复的值, 02 表示 key_part1、 key_part2 两列组合不重复的数。

相关文章:

  • Windows 同步时间服务器批处理
  • 软件测试常见面试题1000问涵盖一千+公司面试软件测试面试题(全网最全)
  • 安全防御——二、ENSP防火墙实验学习
  • Ecal基于wifi下跨机通讯
  • 服务器的操作系统,你选择哪些?
  • 家政预约服务小程序源码系统 线上+线下两种模式 带完整的搭建教程
  • 运动想象 EEG 信号分析
  • 1.OpenResty系列之入门简介
  • JDBC SQL Server Source Connector: 一览与实践
  • MuLogin浏览器如何在一台设备上安全登录和管理多个LinkedIn账户?
  • 基于人工兔算法的无人机航迹规划-附代码
  • 成功解决fatal error: stdatomic.h: No such file or directory #include <stdatomic.h>
  • kmp算法详解+next数组求解
  • 使用Postman工具做接口测试 —— 环境变量与请求参数格式!
  • 浅述边缘计算场景下的云边端协同融合架构的应用场景示例
  • 【翻译】babel对TC39装饰器草案的实现
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • Angular 4.x 动态创建组件
  • create-react-app做的留言板
  • CSS居中完全指南——构建CSS居中决策树
  • Fundebug计费标准解释:事件数是如何定义的?
  • Java 内存分配及垃圾回收机制初探
  • mysql_config not found
  • Nginx 通过 Lua + Redis 实现动态封禁 IP
  • php的插入排序,通过双层for循环
  • spring security oauth2 password授权模式
  • uni-app项目数字滚动
  • 大数据与云计算学习:数据分析(二)
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 让你的分享飞起来——极光推出社会化分享组件
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 如何优雅地使用 Sublime Text
  • 思否第一天
  • !!Dom4j 学习笔记
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (C)一些题4
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (二十四)Flask之flask-session组件
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (七)MySQL是如何将LRU链表的使用性能优化到极致的?
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (十)c52学习之旅-定时器实验
  • (十二)springboot实战——SSE服务推送事件案例实现
  • (未解决)macOS matplotlib 中文是方框
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • .Net Remoting常用部署结构
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .net 程序 换成 java,NET程序员如何转行为J2EE之java基础上(9)
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)
  • .netcore如何运行环境安装到Linux服务器
  • .net企业级架构实战之7——Spring.net整合Asp.net mvc
  • .net用HTML开发怎么调试,如何使用ASP.NET MVC在调试中查看控制器生成的html?
  • .NET与 java通用的3DES加密解密方法