查询优化、连接算法
查询优化
- 重点内容:整个查询的工作过程(逻辑优化、物理优化);中间结果的大小估计;查询计划的IO代价计算
工作流程
- 初始逻辑查询计划生成:将语法分析树转换为关系代数表达式树——逻辑查询计划(使用关系代数)
- 查询重写:将初始逻辑查询计划转换为优化的逻辑查询计划
- 基于代数转换规则
- 估计中间结果大小结果大小
查询代价估计
- 中间关系大小估计
- IO代价估计
- 物理查询计划生成
其中1和2要在内存里完成(磁盘IO代价太高)
中间结果的大小估计
需要用到的一些统计量,也即估计目标:
- T(R):R的元组数
- S(R):R中每个元组的大小(Bytes)
- V(R,A):R的属性A上的不同值数
- B(R): 容纳R所有元组所需的块数
- 例子:R=
A | B | C | D |
---|---|---|---|
cat | 1 | 10 | a |
cat | 1 | 20 | b |
dog | 1 | 30 | a |
dog | 1 | 40 | c |
bat | 1 | 50 | d |
A: 20 byte string; B: 4 byte integer; C: 8 byte date; D: 5 byte string
- 则 T(R)=5, S(R)=37, V(R,A)=3, V(R,B)=1, V(R,C)=5, V(R,D)=4
- W = R1
R2 的大小估计 - T(W) = T(R1) * T(R2)
- S(W) = S(R1) + S(R2)
- W =
(R) 的大小估计 - S(W) = S(R)
- T(W) = s * T(R). (分类讨论:=,
, )
- W = R1
R2 的大小估计. 令 为R1的属性集合, 为R2的属性集合 - 若
,则与R1 R2相同 - 若
,且 R1.A上的值都在R2中, 则 - 一般地:
- S(W) = S(R1) + S(R2) - S(A) (容斥原理)
- V(W,A) = min{V(R1, A), V(R2, A)}(假设满足值集的包含)
- 若
IO代价估计
- 估计目标:指型查询计划所必须读(写)的磁盘块数目
- 影响查询计划IO代价的因素:
- 实现查询计划的逻辑操作符
- 中间结果的大小
- 实现逻辑操作符的物理操作. e.g., 连接操作是用索引连接还是散列连接?
- 相似操作的顺序. e.g., 多关系的连接顺序
- 物理操作符之家你的参数传递方式(流水线还是物化?)
- 流水线:多个操作同时执行,一个操作产生的元组直接通过共享内存传递给其他操作
- 节省IO
- 占用主存,若缓冲区出现“颠簸”则IO增加
- 物化:操作依次执行,并且每个操作的结果(中间关系)都写到磁盘上共其他操作存取
- 通过磁盘物理进行数据传递
- 节省主存空间
- 需要的一些参数:
- B(R):R所需的块数
- f(R):每块可容纳的R的最大元组数
- M:可用的内存块数
- HT(i):索引i的层数
- LB(i):索引i的叶节点所需的块数
连接算法
- 重点内容:每个连接算法的IO代价估计和内存开销
- 连接操作的实现算法 (
): - 嵌套循环连接
- 归并连接
- 索引连接
- 散列连接
连接算法的代价分析
- 影响连接算法代价(I/O)的因素:
- 关系的元组是否在磁盘块中连续存放?
- 关系是否按连接属性有序?
- 连接属性上是否存在索引?
嵌套循环连接代价分析
100个block用来放置R1, 1个block用来放置R2
- 不连续存放,每个元组都需要一次单独的IO: R1
R2 IO代价=T(R1) * (1 + T(R2)) = 50,010,000 - 改进的方法(略)
- 连续存放,设R1和R2被连续存放在块中,则 R1
R2 IO代价=B(R1) / (M - 1) * (M - 1 + B(R2)) = B(R2)
归并连接代价分析
- 沿用嵌套循环连接的例子
- 连续存放且有序,总IO代价=读R1代价+读R2代价=B(R1) + B(R2)=1,000+500=1,500
- 连续存放但无序:需要排序但内存有限. 一种排序方法:两阶段多路归并排序
- 每个元组在两个阶段各有一次读和写,共有4次IO. 故排序IO代价=4 * (B(R1) + B(R2))=6,000
- 考虑连续存放且有序时,需要一次读。故整个流程每个元组经历五次IO,总IO代价=5 * (B(R1) + B(R2)) = 7,500
- Buffer block数的平方必须大于等于排序关系R的块数B(R)
- 归并算法的改进:将第二阶段的排序和join合并进行,省去一次读一次写,总IO代价=3 * (B(R1) + B(R2))
- 要求:R1的chunk数+R2的chunk数
M
- 要求:R1的chunk数+R2的chunk数
索引连接代价分析
- R1(A,C)
R2(C,D) 假设R1.C存在索引
假设R1.C的索引存储在内存中
假设R2连续存放且无序
raed R2: B(R2) IOs
probe index: no IOs
Read matching R1 tuples: ?
- 关键问题:matching tuples 选中率p的估计
- 若R1.C是主键,R2.C是外键,则每个R2tuple在R1中,选中率p = 1
- 若 V(R1,C) < T(R1),则每个R2 Tuple在R1中的选中率p = T(R1)/V(R1,C) = 2
- 总而言之,总代价估计 = B(R2) + T(R2) * p
- 如果R1.C上的Index不能全部放在内存(假设R1.C的一级索引占200块):
- 总Io代价=B(R2) + T(R2) * (Probe index cost + p)
散列连接代价分析
假设R1,R2连续存放但无序
假设共有100个哈希桶
- 第(1)步,内存块数为M,故划分为M-1个桶,每一块对应一个同。最后一块用于读入R1(R2)的一块,计算其中每个元组的h,并将元组复制到对应的块中,共产生2 * (B(R1) + B(R2))次读写
- 第(2)步,按块读取后归并,共产生(B(R1) + B(R2))次读写
- 总而言之,总IO代价=3 * (B(R1) + B(R2))
- 内存要求:min(B(R1), B(R2))
M^2
连接算法的对比总结
R1
R2,连续存放且无序. C是公共属性 假设
对于索引连接,假设索引存于内存中
算法 | 时间代价 | 内存要求 | 适用场景 |
---|---|---|---|
嵌套循环连接(R2 |
相对于内存较小的关系 | ||
归并连接 | 记录有序 | ||
归并连接(改进后) | 记录有序且R1.C>R2.C | ||
索引连接 | 索引存在且有用(太大了不行) | ||
散列连接 | 记录无序且无索引且V(R1.C) |
连接的左右变元选择
- 嵌套循环连接:较小的关系作为左变元
- 归并连接:排序之后较小的关系作为左变元首先读入内存
- 索引连接:有索引的关系作为右变元
- 散列连接:较小的关系作为左变元,散列后读入内存