0%

PostgreSQL优化器白话(6) - 统计局旁的泉水

“咚咚咚。。。咚咚咚”门外传来了敲门声,大明去打开房门一看,进来的原来是自己的同事牛二哥,牛二哥是专门从事数据库查询优化开发的码农,也有十几年的从业经验了,大明感到非常happy,因为这两天给小明讲查询优化器干到有些吃力,今天牛二哥来了,可以帮助自己给小明来讲查询优化器了,于是和牛二哥说:“牛二同志,来了就别走了,今天大腰子加红酒管够,你来帮我个忙。”

牛二哥是大腰子爱好者,听说有大腰子,顿时感觉浑身鸡血翻腾,连忙说:“没问题,不过要帮什么忙?”

大明说:“我的弟弟小明最近在学校要进行数据库原理实践,最近总来问我优化器的问题,可我对优化器也是一知半解,这下你来了,你可以帮我弟弟讲讲优化器了。”

牛二哥痛快的说:“这难不倒我,随时都可以讲。”

大明说:“好嘞,我先给我的弟弟小明打个电话,和他说有优化器高手来了,赶紧过来请教。”

……(此处略去近千字)

小明早就听说过牛二哥的名字,据说对查询优化非常了解,没想到牛二哥竟然和自己的哥哥是同事,于是赶紧来到了大明家,看到牛二哥的洗旧的格子衫和半秃的发型,感觉就像少林寺的扫地僧一样,于是赶紧说:“我这最近正在查看基于代价的优化,感觉付出了很多代价,但收获甚微啊,期望今天能得到牛二哥的指导。”

牛二哥说:“不客气,我是也是受到了大腰子和红酒的诱惑,估计今天也要付出惨痛的代价。。。。你有什么问题尽管问,我捡我会的回答。”看着小明稚嫩的脸庞,牛二哥突然想起了自己刚开始从事数据库内核开发的日子,心底翻腾起一股悲凉感,那是一段不堪回首的血泪史啊,有血、有泪、有。。。

牛二哥突然想到了什么,继续说道:“说到代价,我觉得有个东西是绕不过去的,那就是统计信息和选择率,PostgreSQL数据库的物理优化需要计算各种物理路径的代价,而代价估算的过程严重的依赖于数据库的统计信息,统计信息是否能准确的描述表中的数据分布情况是决定代价的准确性的重要条件之一。”

小明说:“大明和我说过,数据库有很多的物理路径,这些物理路径我们也叫他们物理算子,和逻辑算子不同,物理算子是查询执行器的执行方法,我们只需要计算物理算子每个步骤的代价,汇总起来就是路径的代价了,那要统计信息有什么用呢?”

牛二哥看了看大明,意思是让大明来说,不过大明正在专心的查吃了吗外卖APP,找大腰子和红酒,并没有看到牛二哥的眼神,牛二哥只好尴尬的说:“是的,我们就是要计算一个物理算子的代价,但是物理算子的计算量并不是一成不变的。”说着牛二哥从旁边的书桌上拿来纸和笔,在纸上写了两个SQL语句。

1
2
SELECT A+B FROM TEST_A WHERE A > 1;
SELECT A+B FROM TEST_A WHERE A > 100000000;

然后牛二哥说:“你看,这两个语句可以用同样的物理算子来完成,但是他们的计算量一样吗?”

小明心想:A > 1和A > 1000000000都是过滤条件,经过过滤之后,他们产生的数据量就不同了,这样投影中的A+B的计算的次数就不同了,所以他们的代价应该是不同的,那它和统计信息有什么关系呢?小明感觉灵光一闪,马上说:“我知道了,我在计算物理算子的代价的时候,要知道A > 1之后还剩下多少数据或者A > 1000000000之后还剩下多少数据,如果我们提前对表上的数据内容做了统计,剩下多少数据就不难计算了,所以必须要有统计信息。”

牛二哥点了点头,说:“嗯,通过统计信息,代价估算系统就可以了解一个表有多少行数据、用了多少个数据页面、某个值出现的频率等等,然后就能根据这些信息计算出一个约束条件能过滤掉多少数据,这种约束条件过滤出的数据占总数据量的比例称之为‘选择率’,所谓选择率就是一个比例,它的公式是这样的。”说着牛二哥继续在纸上写了选择率的公式:
选择率=约束条件过滤后的元组数/约束条件过滤前的总元组数

“不过上面的示例有点简单了,实际应用中通常约束条件会比较多,而且比较复杂,通常我们会计算每个子约束条件的选择率,然后就可以根据AND运算符和OR运算符计算它们的综合的选择率,AND运算符和OR运算符的选择率计算是基于概率的,你看这里的概率公式。”说着,牛二哥又继续在纸上写起来。

1
2
P(A+B)=P(A)+P(B)-P(AB)
P(AB)=P(A)×P(B)

“有了这些,我们就可以求解多种类型的约束条件的选择率了,比如。。。”牛二哥继续在纸上画起来:

1
2
3
  P(ssex IS NOT NULL OR sno > 5) 
= P(ssex IS NOT NULL) + P(sno > 5) – P(ssex IS NOT NULL AND sno > 5)
= P(ssex IS NOT NULL) + P(sno > 5) – P(ssex IS NOT NULL) × P(no > 5)

小明觉得牛二哥的进展有点快,赶紧问:“那么统计信息是什么形式的呢?”

牛二哥挠挠头,说:“这个还真是有点麻烦,我们说常用的统计信息的形式就是distinct率、NULL值率、高频值、直方图、相关系数这些,他们分别有不同的作用,比如说distinct率,你可以获知某一列有多少个独立值,这种信息对于像性别这种列就显得特别有用,NULL值率呢?在统计的过程中,NULL值是不好处理的,因此把它独立出来,形成NULL值率,这样在高频值、直方图这些里面就不用考虑NULL值的情况了,高频值属于奇异值,顾名思义,就是出现的比较多的一些列值,去掉了NULL值,再去掉高频值,剩下的值可以用来做一个等频的直方图。。。。”

“呃。。。”小明感觉跟不上牛二哥的速度了,于是说:“有点快,我有点跟不上了。”

这时大明已经点完了外卖,过来说:“别急,外卖没有那么快送到,至于统计信息嘛,主要的还是高频值、直方图和相关系数,实际上我建议还是不要纠结于统计信息有哪些形式,我们只要知道它是用来算代价的就可以了。”

牛二哥对大明说:“这怎么可以,我还没有说统计信息是如何生成的呢,比如它通过了两阶段采样,然后对样本进行统计时使用的统计方法,哪些值可以作为高频值,直方图有几个桶,相关系数是怎么计算的,相关系数在计算索引扫描路径代价的时候怎么用的,而且我和你说,PostgreSQL还出了基于多列的扩展统计信息,多列统计信息分成了哪些类型,分别是什么含义,各自是怎么计算的,还有选择率是怎么结合统计信息计算的,这些我还没说呢。。。”

大明说:“你快闭嘴吧,像你这样讲优化器,岂不是要出一本书。”

牛二哥做痛苦状:“那好吧,统计信息我们就说到这里吧,但是它确实是代价计算的基石,小明同学,你理解了它的作用就可以了。”

小明痛苦的说:“牛二哥,你说了这么多,都不告诉我答案,我感觉要学的内容好多啊。”

大明笑着说:“不用这么痛苦嘛,没有啥事是一顿烧烤不能解决的,如果有,那就是两顿。”然后大明继续神秘的说:“实际上统计信息往往也不准,你想想本来就是采样的结果嘛,样本是否显著压根就不好说,而且随着应用程序对表的更新,统计信息可能更新不及时,那就更会出现偏差。更严重的是,如果我们遇到a > b这样的约束条件,使用统计信息计算选择率也很不好计算,即使算出来,也不准嘛。”

牛二哥说:“是的,统计信息确实也有不准确的问题,我听说有个DBA,他家祖坟旁边出了一口泉水,他爸爸觉得是吉兆,去找风水大师看一看,风水大师掐指算一算,说:你儿子是个DBA,每次数据库性能慢就知道更新统计信息,可是统计信息太水了,都从你家祖坟冒出来了。”

三个人顿时笑做一团,气氛从痛苦又恢复到了活泼,大明说:“学习查询优化器确实还是比较繁琐的,绝对不是我们通过一两次讲解就能让你变得精通的,这个学习的曲线还是比较陡峭的。”

小明点了点头说:“好吧,有了两位师傅,再加上我自己天资聪慧、风流倜傥、玉树临风、高大威武、骨骼惊奇,我相信一定能将优化器搞定。”

大明和牛二哥都晕倒在地上。。。