查询优化概述
数据库优化器的输入是一个关系代数表达式,经过查询优化后,输出一个查询执行计划,并且使输出的执行计划的代价尽可能小
查询优化的步骤可以分为三步:
- 产生一些逻辑上与输入表达式等价的关系代数表达式
- 将所产生的表达式转换成执行计划(一个表达式可能对应多个执行计划)
- 对产生的每个执行计划,估计其执行代价,选择一个代价最小的执行计划输出
代价估计
我们已经知道应该用磁盘传输的块数和寻道次数来估计一个执行计划的代价,而磁盘传输块数和寻道次数一般和关系的块数有关,所以我们只要能估计出进行运算的每个关系的块数,就能估计出整个执行计划的代价
统计信息
如果不借助额外的统计信息,我们是不可能估算出每个关系的块数的,我们对每个关系都额外统计了一些信息,包括关系的元组数、关系的块数、单个元组的字节数、一个块中可以容纳的元组数等
此外,还有一些关于属性的统计信息,设 V(A,r)V(A,r)V(A,r) 表示关系 rrr 中属性 AAA 的不同取值个数,对于关系 rrr 的所有属性 A1,A2,⋯ ,AnA_1,A_2, \cdots ,A_nA1,A2,⋯,An ,我们记录所有的 V(A1,r),V(A2,r),⋯ ,V(An,r)V(A_1,r),V(A_2,r), \cdots ,V(A_n,r)V(A1,r),V(A2,r),⋯,V(An,r) ,根据需要,还可能统计某些属性集的不同取值个数
仅统计属性的不同取值个数,有时还不够精确,因此对于关系上的属性,可能还会保存一张记录其取值分布的直方图
对于直方图的一个取值区间,通常不仅仅保存区间中值的频度,还会保存区间中不同取值的个数,这是为了之后估计运算结果集的大小
对于有索引的关系,还会保存有关索引的统计信息,例如 B+ 树索引的高度、B+ 树索引叶节点数等
统计信息是需要维护的,但如果每次修改关系时,都去更新统计信息,带来的开销太大,并且我们也不需要统计信息时刻保持完全精确,较小的误差完全是可以忍受的,因为我们本来就只是拿统计信息来估算代价
因此一些数据库系统在关系被修改时,一般不立即更新统计信息,而是在某些时间点更新统计信息,例如关系的元组数目发生了显著变化时,利用当前统计信息估算的代价和实际运行的代价相差很大时,数据库系统负载较轻时
另一些数据库系统从不自动更新统计信息,而是由数据库管理员决定何时更新统计信息
此外,统计数据常常是基于整个关系所有元组的一个样本来计算的,这个样本是从所有元组中随机抽样得来的
运算结果集大小的估计
如果进行运算的关系都是数据库关系,那我们可以很容易地估计出这次运算的代价,因为我们有所有数据库关系的统计数据,自然也就知道这些关系的块数
但问题在于,进行运算的关系有可能是一个临时关系,这个临时关系是某个运算的结果,我们需要能够估计出临时关系的块数,由于临时关系的元组的字节数可以根据临时关系的模式计算出,因此我们只需要估计出临时关系的元组数,就能通过计算得出临时关系的块数,也就是说对于一个运算,我们要能估计出结果集的大小
选择运算
对选择运算结果集大小的估计依赖于选择条件
如果选择条件是等值条件,即 σA=a(r)\sigma_{A=a} (r)σA=a(r) ,我们可以假设关系 rrr 的属性 AAA 的取值均匀分布,因此我们可以估计结果集的大小为 nrV(A,r)\frac{n_r}{V(A,r)}V(A,r)nr ,其中 nrn_rnr 为关系 rrr 的元组数,如果在关系 rrr 的属性 AAA 上有直方图,我们可以在直方图中找到 aaa 所在的区间,然后用区间的频度来代替 nrn_rnr ,用区间中不同取值的个数来代替 V(A,r)V(A,r)V(A,r)
如果选择条件是范围条件,例如 σA≥a(r)\sigma_{A \ge a} (r)σA≥a(r) ,我们还是可以假设取值均匀分布,可以估计结果集的大小为 nr⋅a−min(A,r)max(A,r)−min(A,r)n_r \cdot \frac{a-min(A,r)}{max(A,r)-min(A,r)}nr⋅max(A,r)−min(A,r)a−min(A,r) ,其中 min(A,r)min(A,r)min(A,r) 和 max(A,r)max(A,r)max(A,r) 分别表示关系 rrr 的属性 AAA 取的最小值和最大值,通常会包含在统计信息中,如果在关系 rrr 的属性 AAA 上有直方图,我们可以在直方图中找到 aaa 所在的区间,可以估计区间中满足选择条件的元组数为 n⋅a−mM−mn \cdot \frac{a-m}{M-m}n⋅M−ma−m ,其中 nnn 表示区间的频度,mmm 和 MMM 分别表示区间的左右端点,其它区间中的元组要么全部满足选择条件,要么全部不满足选择条件
如果选择条件是多个条件的合取,即 σθ1∧θ2∧⋯∧θn(r)\sigma_{\theta_1 \wedge \theta_2 \wedge \cdots \wedge \theta_n} (r)σθ1∧θ2∧⋯∧θn(r) ,我们可以按之前的方法分别算出 σθ1(r),σθ2(r),⋯ ,σθn(r)\sigma_{\theta_1} (r), \sigma_{\theta_2} (r), \cdots , \sigma_{\theta_n} (r)σθ1(r),σθ2(r),⋯,σθn(r) 的结果集大小,记为 t1,t2,⋯ ,tnt_1,t_2, \cdots , t_nt1,t2,⋯,tn ,我们假设每个条件相互独立,可以估计满足合取条件的结果集大小为 nr⋅t1∗t2∗⋯∗tnnrnn_r \cdot \frac{t_1*t_2* \cdots *t_n}{n_r^n}nr⋅nrnt1∗t2∗⋯∗tn
如果选择条件是多个条件的析取,还是假设每个条件相互独立,可以估计满足析取条件的结果集大小为 nr−nr∗(1−t1nr)∗(1−t2nr)∗⋯∗(1−tnnr)n_r-n_r*(1-\frac{t_1}{n_r})*(1-\frac{t_2}{n_r})* \cdots *(1-\frac{t_n}{n_r})nr−nr∗(1−nrt1)∗(1−nrt2)∗⋯∗(1−nrtn)
如果选择条件是某个条件取反,即 σ¬θ(r)\sigma_{\neg \theta} (r)σ¬θ(r) ,我们可以按之前的方法计算出 σθ(r)\sigma_\theta (r)σθ(r) 的结果集大小,记为 ttt ,可以估计满足取反条件的结果集大小为 nr−tn_r-tnr−t
连接运算
笛卡尔积运算 r×sr \times sr×s 的结果集大小为 nr⋅nsn_r \cdot n_snr⋅ns
估计自然连接运算 r⋈sr \bowtie sr⋈s 的结果集大小时要分情况讨论,设关系 rrr 的属性集为 RRR ,关系 sss 的属性集为 SSS
若 R∩SR \cap SR∩S 是 rrr 的码,则 sss 的每个元组至多跟 rrr 的一个元组连接,可以估计结果集大小为 nsn_sns ,若 R∩SR \cap SR∩S 是 sss 参照 rrr 的外码,则结果集大小等于 nsn_sns
当 R∩SR \cap S