SQL 执行的指导思想是什么? SQL 执行计划的正确依赖选择依赖于什么?统计信息为什么在 SQL 执行中起到关键性的作用?如何才能自动化收集统计信息?让 一起了解 SQL 执行优化的核心底座。
统计信息在数据库当中是很重要的一个东西,华为云高斯GaussDB在这方面做持续的耕耘和持续的一个改进,在统计一些方面做了新的尝试和想法,已经在内部客户当中去推广和试用。今天主要分享一些基本功能和玩法
这次分享主要包含三部分内容,第一部分先介绍统计信息相关的一些知识,统计器到底是什么,统计器是如何被使用,如何被生成的,如何被使用的。
第二部分介绍统计信息究竟是如何收集的,如何做到自动收集。
第三部分是我在使用这个统计信息收集的时候遇到的一些常见的问题。
1. 统计信息简单介绍
SQL 语句的执行,它这个整个过程当中都发生了什么?第一步就是 的用户业务在发送一个 SQL 语句到数据库,它首先是要经过解析器,通过词法分析,语法分析生成一个语法树,拿到了语法树以后,把它交给这个 SQL 的优化器,根据语法树看你要是做要查询哪些表, 要想得要得到什么样的结果。 开始做这个内部的各种执行路径的一些计算,从当中选择一条最优的一个执行路径,生成一个执行计划,把这个执行计划交给这个执行引擎来生成一个查询结果。这个过程就很像 在这个出行打车当中去选择这个出行路径一样。
优化器以前是有一种叫基于规则的优化器,它就类似于以前在没有智能手机之前,拿着纸质地图去出行一样,只有具体的一些规则,就是从哪到哪,哪个路径最短,就只能就是这样去选择。它没有路况信息,也不知道哪个路发生了拥堵,哪个路是不是有这个事故发生, 这个选择就是只是基于一些最简单的规则,就是一个试试看有没有唯一索引,有没有这个主键, 优先选择这个索引来看。但是它由于没有收集这个统计信息, 它没有办法考虑大表大小,现在的优化器都是基于成本的优化器就类似于现在这个地图 APP 进行打车一样,它是根据实时路况以及司机的些数据, 就知道了这个路是不是哪些路段有拥堵发生,哪些路段有事故发生,从而 得到了一体,从而就会得到一个最优的一个执行路径。相对于优在优化期里面也是很差不多的一个东西。
优化器先进行这个列上的这个统计信息收集,知道了这个表大小,知道了列宽,知道了这些数据以后, 开始进行每条执行路径的成本的一个估算。数据库里面首先就是估算成本分两方面,一个是 CPU 的一个代价的估算,一个是 IO 代价的一个估算。基于这个统计信息进行每一条路径的一个代价计算,从而选择一个最终最优的。 现在的税务产品基本上全都是基于成本的优化器,这个优化器模型统计信息的收集也显得很重要,就像打车一样,如果没有这个实时路况信息,那出行就会造成很大的一个麻烦。
接下来看一下统计信息在思维语句当中的一个这个层次关系,因为 进行数据库操作都使用SQL 语言,它也是一个结构化查询语言,它是一个高度的,并且是非过程化的一个编程语言。用户是在这个高层次进行数据结构上的一个操作,用户只需要关心我需要干什么就行了,不需要关心具体你怎么干。 像这个路径的一些选取代价的一个估算,具体做什么样的操作,如何去排序,如何去进行数据扫描,这些完全都是由数据库内部自动完成的。既然是由于数据库内部自动完成了,因为 有很多不同的路径去选择,不同的操作方式去选择,那如何选择一个最优的?就是一个问题。 它分三个层面,第一个层面就是先进行一些统计信息的一个收集,它是基于这个随机采样的随机采集一些部分数据来生成这个统计信息。有了这个统计信息以后, 根据 SQL 语句里面要查询哪些表要做关联, 在这些算子上进行代价的一个估算,每条路径都这样去算代价,算完以后生成代价最小执行计划,作为最终的这个计划去执行。 统计信息是生成所最优执行计划的一个前提。
先举一个例子, 这是一个是TBC的一个查询语句, 它做两个表的关联,一个是 order 表,一个是 line item 表的一个关联。在没有收集统计信息的一个情况下,这个是生成的一个计划,首先就是这个是对两个表进行扫描,这个 Lion item 表是是在下面,它是先把这个 Line item 表,它实际上是一个比较大的一个表,占的条数要比这个 order 表要大很多。这个没有收统计信息的时候,这个它把这个大表放到了加载到了这个内存, 执行耗时是 2878 毫秒。 收集完统计信息之后,再看这个执行计划,order 表就在最下面了,把它加载到内存,就把这个小表加载到了这个内存,执行时间是 1971 毫秒, 有了统计信息之后, 正确地把小表这个加载到了内存,得到了一个更优的一个执行性能。 在基于代价估算的这优化启模型当中,统计信息会直接影响到这个执行计划的一个选择。
知道了统计信息对于优化器很重要,那么统计信息它是如何被优化器使用的?
先看一下统计信息都包含哪些东西。统计信息分两个层面,一个是表级的统计信息,一个是列级的一个统计信息。表级是描述表的一些大小,具体的有real pages 就是表在磁盘当中占用的这个物理的页面数,还有就是 real tuples 就是表的实际的数据条数。这样就知道表的一个规模。列级统计信息它包含很多值,比较典型的就是这 4 个值。第一个就是叫distinct,就是唯一值的个数,就是把这一列进行去重以后,最终剩的剩下的唯一值的一个个数有多少个。还有就是空值的一个占比。
最重要的是下面的两个,一个叫MCV,就是 most 的 common value,就是 把它叫做高频值,就是主要是它描述这个数据的一个重复情况。简单说就是要看一下这个列当中重复最多的数都有哪些。还有一个就是直方图,直方图用于来描述数据的一个分布情况,生成方式就进行数据采集以后,按一定的步长去打点,抽出一些按步长去抽出一些数据来。有了这些基本的统计信息以后,优化器就是拿一些这些信息来进行这个 SQL 代价的一个估算。
接下来 看一下优化器到底是如何去使用这些东西的。
第一个要看表集的统计信息是如何影响表大小估算的,刚才已经看了表集的统计信息,描述了这个表的数据规模,其实主要有这个页面数和条数这两个。比如说做一个查询叫 select sharing from 这个表,看一下它的查询计划, sequence scan 进行一些数据扫描,它估算出来的条数也是1万,和这个1万是相等的,但它实际是怎么计算的?就是它是在统计机器生成的时候,用这个条数乘以这个除以这个物理的页面数,把它就叫做页面的元组密度,就是平均每个页面大概有多少条数据。 为什么要乘以实际的页面数?就是因为生成统计信息以后,这个数据可能还会再变,这个表的页面可能还在增加,那么进行代价估算的时候,是基于前一步生成的这个统计信息, 就是拿前一步是这个元组密度乘以实际的页面数来估算出当前的一个大概的表大小,就是通过这样计算刚好也得到了是这个1万条。 如果这个原组页面密度估算得不准,那么它就会影响到这 表大小的一个估算会出现一个偏差。
接下来 看进行如何进行等值比较的一个估算。
等值比较就是用这个 SQL 语句,从这个表去查询这个 string 1的这个值,等于这个 CRA这个值,看它有这个多少条。打出来这个计划,也是因为这上面没有索引,先走的是顺序扫描,估算出来这个值是有 30 条,那么这个 30 怎么来的?就是基于上面的这个统计信息,这个是 查询出来的这个 MCV 也叫高频值的一个统计信息,它包含两方面的一个内容,第一方面就是这个 most common value 就是具体的值。