数据集合D的分布由D上不同取值的频次构成。设D为表6-9在Grade列上的投影数据,该列有3个不同取值Grade = 1, 2, 3,其频次分布见表6-10。这里,将Grade取值的个数简称为NDV(Number of Distinct Values,不同值的数量)。
表6-9 Grade属性分布
| Sno | Name | Gender | Grade |
| 001 | 小张 | 男 | 1 |
| 002 | 小李 | 男 | 2 |
| 003 | 小王 | 男 | 3 |
| 004 | 小周 | 女 | 1 |
| 005 | 小陈 | 女 | 1 |
表6-10 Grade频次分布
| Grade | 1 | 2 | 3 |
| 频次 | 3 | 1 | 1 |
D可以涉及多个属性,将多个属性的分布称为联合分布。联合分布的取值空间可能十分庞大,从性能的角度考虑,数据库不会保存D的联合分布,而是将D中的属性分布分开保存,比如,数据库保存{ Gender=’男’}、{ Grade=’1’}的频次,而并不保存{ Gender=’男’, Grade=’1’}的频次。这种做法损失了D上分布的很多信息。在随后的选择率与数据分布小节的内容将看到,在系统需要的时候,openGauss将采取预测技术对联合分布进行推测。虽然在某些情况下,这种推测的结果可能与实际出入较大。
数据分布的数据结构对于理解数据库如何存储该信息尤为关键。一般来说,KV(key-value)键值对是描述分布最常用的结构,其中key表示取值,value表示频次。但在NDV很大的情况下,key值的膨胀使得KV的存储与读取性能都不高。为提高效率,openGauss实际采用“KV向量+直方图”的混合方式表示属性分布。
数据分布的逻辑结构:高频值频次采用KV存储,存储结构被称为最常见值;除高频值以外的频次采用等高直方图(equal-bin-count histogram,EH)描述。实现中,openGauss会将频次最高的k( k=100 )个key值放入MCV,其余放入直方图表示。
值得注意的是,等高直方图会将多个值的频次合并存放,在显著提升存取效率的同时,也会使得分布模糊化。但在后续章节可以看到,相对于低频值,高频值对计划代价的估算更为关键。因此,采取这种以损失低频值准确性为代价,换取高性能的混合策略,无疑是一种相当划算的做法。
数据分布的存放位置:在openGauss中,MCV、直方图等信息实际是放在系统表PG_STATISTIC中的,表定义如表6-11所示。
表6-11 系统表PG_STATISTIC定义
| starelid | staattnum | stanullfrac | stakind1 | stanumbers1 | stavalues1 | Stakind2 | …… |
| 0001 | 1 | 0 | 1 | {0.2851, 0.1345} | {1, 2} | 2 | |
| 0001 | 2 | 0 | 1 | {0.1955, 0.1741} | {数学, 语文} | 2 |
表6-11中的一条元组存储了一条属性的统计信息。下分别对元组的属性意义进行解读。
(1) 属性starelid/staattnum表示的表OID和属性编号。
(2) 属性stanullfrac表示属性中为NULL的比例(为0表示该列没有NULL值)。
(3) 属性组{ stakind1, stanumbers1, stavalues1}构成PG_STATISTIC表的一个卡槽,存放表6-12中的一种数据结构类型的信息。在PG_STATISTIC表中有5个卡槽。一般情况下,第一个卡槽存储MCV信息,第二个卡槽存储直方图信息。以MCV卡槽为例:属性“stakind1”标识卡槽类型为MCV,其中“1”为“STATISTIC_KIND_MCV”的枚举值;属性stanumbers1与属性stavalues1记录MCV的具体内容,其中stavalues1记录key值,stanumbers1记录key对应的频次。上例中取值“1”的频次比例为0.2851,“2”的频次比例为0.1345。
表6-12 系统表PG_STATISTIC说明
| 类型 | 说明 |
| STATISTIC_KIND_MCV | 高频值(常见值),在一个列里出现最频繁的值,按照出现的频率进行排序,并且生成一个一一对应的频率数组,这样就能知道一个列中有哪些高频值,这些高频值的频率是多少 |
| STATISTIC_KIND_HISTOGRAM | 直方图,openGauss数据库用等频直方图来描述一个列中数据的分布,高频值不会出现在直方图中,这就保证了数据的分布是相对平坦的 |
| STATISTIC_KIND_CORRELATION | 相关系数,相关系数记录的是当前列未排序的数据分布和排序后的数据分布的相关性,这个值通常在索引扫描时用来估计代价,假设一个列未排序和排序之后的相关性是0,也就是完全不相关,那么索引扫描的代价就会高一些 |
| STATISTIC_KIND_MCELEM | 类型高频值(常见值),用于数组类型或者一些其他类型,openGauss数据库提供了ts_typanalyze系统函数来负责生成这种类型的统计信息 |
| STATISTIC_KIND_DECHIST | 数组类型直方图,用于给数组类型生成直方图,openGauss数据库提供了array_typanalyze系统函数来负责生成这种类型的统计信息 |
注意,数据分布和PG_STATISTIC表中的内容不是在创建表的时候自动生成的,其生成的触发条件是用户对表进行了analyze操作。




