MySQL统计信息以及执行计划预估方式初探
数据库中的统计信息在不同(精确)程度上描述了表中数据的分布情况,执行计划通过统计信息获取符合查询条件的数据大小(行数),来指导执行计划的生成。
在以Oracle和SQLServer为代表的商业数据库,和以开源的PostgreSQL为代表的数据库中,直方图是统计信息的一个重要组成部分。
在生成执行计划的时候,通过统计信息以及统计信息的直方图来预估符合条件的数据行数,从而影响执行计划的生成。
统计信息对执行计划的影响,具体体现在:索引的查找与扫描,多表连接时表之间的驱动顺序,表之间的JOIN方式,以及对sql查询语句的资源分配等等。
但是在MySQL数据库中,执行计划的方式相对简单,表之间的JOIN只有LOOPJOIN一种方式,且没有并行执行计划等,也就说通过预估结果集的行数对执行计划的影响有限。
但是对于某些情况,依旧需要预估的方式来指导执行计划的生成,
比如常见的多表连接时驱动顺序,多数情况下是小表驱动大表(不完全一定)的方式来实现查询的,因此MySQL中一样需要预估来指导执行计划的生成。
不过MySQL中的统计信息相对来说简单很多,只有一个cardinality信息来预估索引的选择性(show index from table),
索引统计信息不包含直方图的信息,非索引列也不会生成直方图,也就是无法通过直方图来预估查询数据的大小,mysql是通过其他方式来实现预估的。
对于有直方图的数据来说,直方图为预估提供了重要的依据,对于没有直方图的MySQL,执行计划是如何预估的?预估的准确性有如何?
笔者在研究这个问题的时候,一开始也遇到不少疑惑的地方,还是看了博客园大神的问题才得以释惑,后面会给出链接。
首先通过例子,通过一个非常简单的查询来观察一个有意思的现象。
新建测试表,测试表如下:
create table test_statistics
(
id int auto_increment primary key,
col2 varchar(200),
col3 varchar(200),
create_date datetime,
index idx_create_date(create_date)
)ENGINE=InnoDB;
存储过程通过循环插入数据,调用存储过程生成100W行数据(100W行的数据,在实际应用中已经是一个非常小的数据量了),create_date字段上生成一个范围之内的随机时间。
CREATE DEFINER=`root`@`%` PROCEDURE `p_insert_test_data`(
IN `loop_count` INT
)
BEGIN
declare i int;
while (loop_count>0)
do
insert into test_statistics(col2,col3,create_date) values (uuid(),uuid(), DATE_ADD(sysdate(), INTERVAL -rand()*2400 hour));
set loop_count = loop_count -1;
end while;
END
写入测试数据完成之后,进行如下两个查询做测试。
简单地使用select count(1)的来做测试
首先看第一个查询:查询的时间范围是: where create_date>'2017-11-01 12:00:00' and create_date<'2017-11-01 16:00:00'
可以发现:explain预估的行数,与实际行数完全一致。
继续第二个查询,扩大查询的时间范围,查询的时间范围是:where create_date>'2017-11-01 12:00:00' and create_date<'2017-11-03 16:00:00'
可以发现,此时的explain执行计划的预估,与实际行数出现了严重的偏差
为什么第一个查询做到了精确的预估,而第二个查询的预估出现严重的偏差?
这一点要从预估的计算方式入手来说。
首先,第一个查询和第二个查询,唯一的不同是,第二个查询的时间范围放宽了,为什么时间放宽之后,执行计划的预估的准确性就大大下降?
既然是“预估”,就一定是存在误差,只不过是误差大与小的问题,误差的大下与具体的预估的方式有关。
任何预估的实现,都是以一种在不同程度上“以偏概全”的方式进行的,比如SQL Server是以对相关数据page的通过某种百分比来取样,然后存储在直方图中做预估依据的。
当然,这种“以偏概全”的预估方式,是在性能与精确度之间权衡折中的结果.
在考虑收集统计信息对性能和资源影响的前提下,预估策略各种方式或者代价尽可能减少对预估产生误差的因素,关于直方图的生成这里不细说。
对于没有直方图的MySQL,它是是在执行的时候,通过扫描符合查询条件的部分数据页后做预估统计的.
MySQL是在查询的时候,直接对查询条件范围内的数据页,取一定比例样本做统计之后预估的,但是这里取样的数据页面有一定的限制,不会无限制取样做统计预估。
如果符合条件的数据页超出了预定的范围,则会取部分页进行预估,而不是全部页(为什么不是全部样做统计预估,原因就不用说了吧)。
比如下图中,不管是聚集索引还是二级索引(非聚集索引),理论上说都是一颗平衡树,暂不探究其细节。
假如符合条件的数据是一个范围,位于两个矩形框之间。矩形框分别是范围的左右节点,中间可以想象成多个叶子节点
参考zhanlijun大神的文章,
上述参考链接中得知,MySQL在5.5之后的预估原理如下:
其预估扫描的数据页分别是前后两个数据页,以及从左边开始连续8个数据页,得到平均每个page的行数,根据总的page个数预估出这个范围的数据行数。
具体说,也就是取左右两个叶子节点,以及从左叶子节点开始连续8个页的数据做统计,中间可能有多个数据页,但也会被忽略,这就是上面提到的“以偏概全”的方式。
这里面就存在一个最明显的问题,也就是符合条件的数据页面与预估时候采集的页面的大小关系。
如果符合条件的数据页的分布少于10个,当然在预估的时候,会全部扫描这些page,当然预估是完全精确的,这也是第一个查询执行计划预估的实际行数完全不一致的原因。
如果符合条件的数据页的分布大于10个,当然在预估的时候,会部分扫描这些page,预估的误差情况就此产生,这也是第二个查询执行计划预估的实际行数差异较大的原因。
当然MySQL的每个版本可能都有所改进或者差异,笔者并没有从源码中找到具体的算法,当前测试的是5.7.20版本。
但目前仍不清楚,
1,在create_date字段上,时间是按照DATE_ADD(sysdate(), INTERVAL -rand()*2400 hour)生成的,从整体分布看,基本按照时间均匀分布的.
理论上根据这种方式推到,得到的预估结果偏差应该不会很大,但尚不清楚为什么预估与实际存在如此大的差异。
2,尝试找到预估值从精确到产生差异的临界点,通过查询实际行数,根据key_len的值以及B树索引的存储原理(二级索引叶子节点存储的二级索引的key值+聚集索引的key值).
理论上计算出来当前查询一个大概的取样的page个数,发现这个值预报理论上的10个page差异较大,可能是推到方式有问题,或者是MySQL预估本身有一些不知道的细节问题。
3,没有详细翻MySQL的源码,尚未找到具体的实现细节。
对于有直方图的数据库来说,直方图的信息也不是没有代价,或者是万能的,直方图也有直方图的局限性,这里暂不表述。
对于尚没有直方图的MySQL数据库来说,其预估原理是每次查询的时候进行对相关的数据页面进行采样预估的,而不是从直方图中获取到预估信息的,这是一个很消耗性能的操作。
详情参考:http://www.orczhou.com/index.php/2013/04/how-mysql-choose-index-in-a-join/
这可能会导致MySQL不适合做较大数据量或者较为复杂的JOIN操作,当然这也取决于具体的业务设计方案以及对数据的依赖程度,或者主观上的查询提示操作。
说这句话是冒着被MySQL的大神以及粉丝们怒喷的风险的。
关于MySQL的预估的知识点,搜索到的文章并不是很多,也拘泥于个人的认识有限,也希望对这方面有关注的大神多多指点。
据说MySQL在8.0之后的版本中会加入直方图信息,以及其他JOIN方式(除了LOOP JOIN),这可能对性能上有比较大的帮助。
参考链接:
https://www.cnblogs.com/LBSer/p/3333881.html
http://www.orczhou.com/index.php/2013/04/how-mysql-choose-index-in-a-join/
MySQL统计信息以及执行计划预估方式初探的更多相关文章
- (3.14) set statistics io/time/profile /SET SHOWPLAN_ALL ON详解统计信息与执行计划
SQL Server读懂语句运行的统计信息 SET STATISTICS TIME IO PROFILE ON 执行计划详细描述请参考(读懂执行计划) 对于语句的运行,除了执行计划本身,还有一些其他 ...
- SELECT TOP 1 比不加TOP 1 慢的原因分析以及SELECT TOP 1语句执行计划预估原理
本文出处:http://www.cnblogs.com/wy123/p/6082338.html 现实中遇到过到这么一种情况: 在某些特殊场景下:进行查询的时候,加了TOP 1比不加TOP 1要慢(而 ...
- SQL Server统计信息偏差影响表联结方式案例浅析
我们知道数据库中的统计信息的准确性是非常重要的.它会影响执行计划.一直想写一篇关于统计信息影响执行计划的相关博客,但是都卡在如何构造一个合适的例子上,所以一直拖着没有写.巧合,最近在生产环境中遇到 ...
- MySQL学习系列2--MySQL执行计划分析EXPLAIN
原文:MySQL学习系列2--MySQL执行计划分析EXPLAIN 1.Explain语法 EXPLAIN SELECT …… 变体: EXPLAIN EXTENDED SELECT …… 将执行 ...
- MySQL 索引管理与执行计划
1.1 索引的介绍 索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息.如果想按特定职员的姓来查找他或她,则与在表中搜索所有的行相比,索引有助于更快地获取信息. ...
- MYSQL与TiDB的执行计划
前言 这里采用了tpc-h一个数据库的数据量来进行查询计划的对比.并借助tpc-h中的22条查询语句进行执行计划分析. mysql采用的是标准安装,TiDB采用的是单机测试版,这里的性能结果不能说明其 ...
- MySQL统计信息简介
作者:王小龙@网易乐得DBA 原文地址: http://mp.weixin.qq.com/s/698g5lm9CWqbU0B_p0nLMw MySQL执行SQL会经过SQL解析和查询优化的过程,解析器 ...
- python/MySQL(索引、执行计划、BDA、分页)
---恢复内容开始--- python/MySQL(索引.执行计划.BDA.分页) MySQL索引: 所谓索引的就是具有(约束和加速查找的一种方式) 创建索引的缺点是对数据进行(修改.更新.删除) ...
- SQL Server统计信息:问题和解决方式
在网上看到一篇介绍使用统计信息出现的问题已经解决方式,感觉写的很全面. 在自己看的过程中顺便做了翻译. 因为本人英文水平有限,可能中间有一些错误. 假设有哪里有问题欢迎大家批评指正.建议英文好的直接看 ...
随机推荐
- video自动填充满父级元素
想要video能自动填充慢父div的大小,只要给video标签加上style="width= 100%; height=100%; object-fit: fill"即可. obj ...
- 【leetcode】429. N-ary Tree Level Order Traversal
problem 429. N-ary Tree Level Order Traversal solution1:Iteration /* // Definition for a Node. class ...
- ES5和ES6中关于import & export的书写方式的区别
ES6中输出变量的写法 情景1:单个变量 输出 export const less = 'less' 引用 import {less} from '../index.js' 情景2:多个变量 输出: ...
- java-Calendar类
1.Calendar类的概述和获取日期的方法 * A:Calendar类的概述 * Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR.MONTH.DAY_OF_MONTH.HOUR ...
- /etc/hosts和/etc/hostname区别
/etc/hosts主要是ip和域名的对应 /etc/hostname主要是本地主机域名(本地主机名修改过后需要重启服务器才能生效) 如果我想在另一台linux主机里面使用域名访问上面这台主机A,只需 ...
- zombodb 低级api 操作
zombodb 低级api 允许直接从zombodb 索引中进行insert.delete 文档,同时保留了mvcc 的特性,但是数据没有存储在 pg 中,但是也带来数据上的风险,我们需要注意进行es ...
- .htaccess FollowSymlinks影响rewrite功能
Thinkphp的框架的根目录的.htaccess是这样写的: <IfModule mod_rewrite.c> Options +FollowSymlinks RewriteEngine ...
- 第十一章 IO流
11.IO流 11.1 java.io.File类的使用 1课时 11.2 IO原理及流的分类 1课时 11.3 节点流(或文件流) 1课时 11.4 缓冲流 1课时 11.5 转换流 1课时 11. ...
- Java_框架面试题
Java_框架面试题 欢迎来我Git上分享您的优秀建议 1.Spring框架分为哪七大模块,各模块的主要功能作用是什么? 七大模块,如下: 1. Spring Core: Core封装包是框架的最基础 ...
- ueditor的简单用法
先粘贴未使用ueditor之前的代码: <body> <label for="input_content">作答区:</label> <t ...