当前位置: 首页 > 科技观察

MySQL执行计划中的行到底是什么,你真的了解了吗?

时间:2023-03-12 05:43:27 科技观察

1。事件背景是周五下班,或者DBA同学已经下班找不到了,或者考虑到我在公司维护数据库中间件,对数据库问题有一定的经验,邢总说是讨论一个奇怪的sql执行计划问题,本来还有点信心,但是经过简单的上下文同步和一些操作演示和讨论,我也觉得这种情况比较奇怪,比较迷惑。..原情况下完全同步的性价比不高。我简单描述一下,只要能符合读者的认知即可;情况大致是这样的:一张表除了主键之外,还涉及另外三个索引,A索引,B索引,+B复合索引,使用不同的索引explain显示的预估行数的结果是不同的,情况如下,第三项很迷惑:索引情况的查询计划实际结果中的实际行数,估计扫描行数存在两个字段A和B的独立索引只命中A索引搜索(其中a=xxx)2626带有A和B字段的独立索引只命中B索引(其中b=yyy)256255存在A和B两个字段独立索引命中A索引和B索引(其中a=xxxandb=yyy)94有A+B两个字段的复合索引命中A+B复合索引(其中a=xxxandb=yyy)99研究了很久的底层数据库。多年前潜心研究《SQL Server技术内幕》系列书籍,略有积累。这些书籍是:T-SQL编程、T-SQL查询、存储引擎、查询调整与优化(如果你使用SQLServer,推荐阅读这些书籍);SQLServer执行计划调优虽然有一定的认知储备,但那天讨论的毕竟是MySQL,张冠李戴不是技术人的作风、原理和现象不太确定。2.相关技术简述2.1B+树组织结构在这种索引情况下,MySQL以B+树结构组织和管理索引页和数据页。,4页),索引页是指只包含索引记录的页面(上图中其余的其他页面)。索引是排序的,页面的组织和管理也依赖于这个顺序。聚集索引的叶子节点(左)是数据页,非聚集索引的叶子节点(右)不是数据页,最终在非聚集索引中检索到的结果就是key聚簇索引,不是数据页的rowID;这样的低耦合设计是有好处的,比如在压缩空间的时候,会避免页面中很多记录的变化。2.2执行计划什么是执行计划?执行计划是数据库的查询优化器根据用户输入的SQL语句,以及它内部的执行策略和统计信息,选择一个它认为执行效率最好的计划,然后用这个计划得到数据。我们通常通过执行计划来查看数据库如何处理SQL语句,分析性能瓶颈。查看执行计划:在select前加上explain关键字。执行后可以看到下图中的执行计划信息。下表是对执行计划信息的各个字段的简单介绍。本文的重点是行字段。3.官网如何解释行3.1信息显示,从官网可以看到如下描述行(JSONname:rows),其中rows列表示MySQL认为执行查询必须检查的行数。对于InnoDB表,这个数字是一个估计值,可能并不总是准确的。汉化:rows列表示MySQL认为执行查询必须检查的行数。对于InnoDB,这个数字是一个估计值,不一定准确。3.2官网上的思路很精辟,但是一些关键的内部设计没有提到。4.Rows的解释A4.1信息显示,当查询优化器决定使用全表扫描对表执行查询时,执行计划中的rows列表示表中估计的行数。如果使用索引来执行查询,则执行计划的行列表示扫描的预期索引行数。4.2扫描全表时:只在数据页中扫描的行数是否对应于预期扫描的行记录数?在索引扫描期间:rows对应于预期被扫描的索引记录行数。如果是聚簇索引,是索引页的行数+数据页的记录行数吗?如果是非聚集索引,这个行数是否只是非聚集索引页中的索引记录行数?既然是扫描,为什么说数据不准确呢?为什么这里没有提到统计数据?SQLServer中执行计划评估扫描的行数与统计信息有关,不是MySQL吗?5.RowsB5.1的解释信息表明,如果查询优化器决定使用全表扫描对表执行查询,则执行计划的rows列表示期望扫描的行数。如果使用索引来执行查询,则执行计划的行列表示要扫描的预期索引行数。这可能是一个准确的值,也可能是一个估计值,由指数跳水计算得出,并根据指数统计数据进行估计。5.2思路1和2与A类似,不再赘述,但第三条信息很重要,为前面的问题提供了一些线索。MySQL也会使用统计信息来选择执行计划,统计信息会有错误;但什么是指数跳水?统计信息的实现机制是什么?6.indexdiv的解释6.1什么是Indexdive获取index对应的B+树区间的最左边和最右边的记录,然后计算这两条记录之间有多少条记录(当数量为记录量小)要准确计算,很多情况下只能估计)。MySQL调用这种直接访问索引对应的B+树的方法来计算一定范围区间对应的索引记录数作为Indexdive。有一个与Indexdive相关的配置参数eq_range_index_dive_limit。它的作用大致是这样的:当where语句的in条件中的参数个数小于这个值时,MySQL通过Indexdive来估计扫描的行数,非常准确。当where语句的in条件中的参数个数大于等于这个值时,MySQL会使用另一种索引统计的方法来估计扫描的行数,误差较大。默认值在不同版本的MySQL中是不同的,可以根据需求场景进行调整。6.2思考从这些信息中又可以看出,使用Indexdiv会更准确地估计扫描行数,但是估计成本比较高,适合数据量小的情况。索引统计的估计成本比较低,适用于数据量较大的情况。但是,如果使用指标统计,则评价不准确,甚至误差很大。为什么误差大,误差有多大?接下来,我们将收集相关信息进行了解。7.统计信息说明7.1统计信息介绍查询优化是建立在成本统计分析的基础上的。合理的成本模型和准确的成本统计信息决定了查询优化的质量。MySQL的成本模型主要取决于IO和CPU。IO主要与数据量和缓存有关,CPU主要与参与排序比较的记录条数有关。因此,统计信息的指标主要是数据量和记录数,如:表扫描:全表扫描统计包括数据量和记录数。indexscan:索引统计,索引键值的分布,即基数。范围扫描:索引范围扫描统计,一定范围内的记录数和数据量。7.2查看索引统计的统计信息innodbmysql.innodb_table_stats:存储表的统计数据,每条记录对应一个表的统计数据mysql.innodb_index_stats:存储索引的统计数据,每条记录对应一个索引的统计数据进行统计。以innodb_table_stats表为例,各列的说明:列名说明database_name数据库名table_name表名last_update这条记录的最后更新时间n_rows表的记录数n_rows表的聚集索引占用的页数tableclustered_index_sizesum_of_other_index_sizes该表其他索引占用的页数显而易见,n_rows很重要,那么它的值是怎么计算的呢?7.3统计信息抽样估计执行计划的行数取决于n_rows。InnoDB中n_rows的统计如下:按照一定的算法(不是纯随机的)选择若干叶子节点页,计算出每页中的主键值记录数乘以主键的记录数一个平均页中的值除以所有叶节点的个数,即表的n_rows值。由此可见,n_rows值是否准确取决于统计时采样的页数。它由innodb_stats_persistent_sample_pages设置。值越大,统计越准确,但耗时也会增加;设置越小,统计越不准确,但统计的时间越少,具体要看实际情况。7.4统计信息的更新在MySQL中,以下几种情况会触发统计信息的更新:自上次更新统计信息以来,变化的行数超过一定值时自动更新(瞬时:1/16,持久:1/10)analyzetablecreatetable/truncatetable会初始化统计信息查询information_schema.tablesinformation_schema.statistic(innodb_stats_on_metadata=ON)第一种是变化的记录数超过表大小的10%,那么服务器会自动触发异步统计数据的计算;其他方式是手动触发。8.总结本文主要基于日常工作中的一次技术交流,以执行计划中的行为主线,收集资料,梳理认知;但知识是无限的,到现在还没有能够探索出与预期不符的实际情况计算过程只是为了对这个知识点有一个浅层的系统理解,有助于继续分析和探索其内部故事;同时希望本研究中的记录能够对您有所帮助。参考:https://dev.mysql.com/doc/refman/8.0/en/explain-output.htmlhttps://blog.csdn.net/u022812849/article/details/120145037https://www.cnblogs.com/ldws/p/12349502.htmlhttps://baijiahao.baidu.com/s?id=1740652003821645078&wfr=spider&for=pchttps://mp.weixin.qq.com/s/-7qU1MPlBin4XdjhzTG-TQhttps://baijiahao.baidu.com/s?id=1740652003821645078&wfr=spider&for=pc本文转载自微信公众号“结构染”,可通过以下二维码关注。转载本文请联系【结构染】公众号作者。