(一)查询代价的度量
一般来说,我们使用传送磁盘块数以及搜索磁盘次数来度量查询计算计划的代价。
假设:
——磁盘子系统传输一个块的数据平均消耗时间;
——磁盘块平均访问时间(磁盘搜索时间加上旋转延迟);
——块数;
——磁盘搜索次数,
那么总的时间开销为。
当今高端磁盘的典型数值通常是和
,所以在优化层面上可以根据实际情况(块数和搜索次数)考虑牺牲其中一种时间去换取另一种时间。
(二)选择运算
(三)排序(外部排序归并算法)
归并排序包括排序和归并两个阶段:
(1)建立多个排好序的归并段。每个归并段都是排好序的,但仅包含关系中的部分记录。
(2)对归并段进行归并。归并段的数目需要小于内存块数,此外剩下的空间还应能容纳结果的一个块。
假设:
——内存缓冲区中可以用于排序的块数,即内存缓冲区能容纳的磁盘块数;
——包含关系
中记录的磁盘块数;
——归并阶段中为归并段所分配的缓冲块(即每次从归并段读取
块数据)。
归并过程如下:
头个归并段进行归并得到一个归并段作为下一趟的输入。接下来的
个归并段类似地进行归并,如此下去,直到所有的初始归并段都处理过为止。此时,归并段的数目减少到原来的
,如果归并后的归并段数目仍大于等于
,则以上一趟归并创建的归并段作为输人进行下一趟归并。每一趟归并段的数目均减少为原来的
。如有需要归并过程将不断重复,直到归并段数目小于
,此时作最后一趟归并,得到排序的输出结果。
代价分析:
在排序阶段要读入关系的每一数据块并写出, 共需,次磁盘块传输;初始归并段数为
。由于每一趟归并会使归并段数目减少为原来的
,因此总共所需归并趟数为
。但是最后一趟可以只产生排序结果而不写入磁盘。所以关系外排序的磁盘块传输总数为:
。
在排序阶段,我们需要次磁盘搜索以读取和写回数据;在归并阶段,每一趟归并需要作
次磁盘搜索以读取和写回数据,除了最后一趟以外(因为我们假定最终结果不写回磁盘)。则磁盘搜索的总次数为:
。
(四)连接运算
(1)嵌套循环连接
假设关系为连接的外层关系,关系
为连接的内层关系,那么可以通过两层的嵌套循环来实现
和
进行拼接(
和
分别表示
和
的元组)。
算法表示如下:
for each 元组t_r in r do begin
for each 元组t_s in s do begin
测试元组对(t_r, t_s)是否满足连接条件;
如果满足,把t_r·t_s加到结果中;
end
end
假设:
——外层关系;
——内层关系;
——
中的元组数;
——
中的元组数;
——
中的磁盘块数(
);
——
中的磁盘块数(
)。
(a)在最坏的情况下,即缓冲区只能容纳每个关系的一个数据块(两个关系都只能有一个块在内存),
对于每次外层循环:
都需要取出外层关系中的一个块以及内层关系
中的所有块,所以需要
次块传输。
但读取都只需要一次磁盘搜索(因为是顺序读取),读取
一共需要
次磁盘搜索,所以总的磁盘搜索次数为
。
(b)在最好的情况下,即内存有足够的空间同时容纳每个关系的所有块,
此时每一个数据块只需读一次,即只需次块传输。
磁盘搜索也只需开始读取关系时的两次(放入内存后遍历则无需使用磁盘搜索)。
(c)如果其中一个关系能完全放入内存中,那么把这个关系放进内层是有好处的,因为这样每次外层循环中内层关系只需要取一次。
(2)块嵌套循环连接
因缓冲区太小而内存无法完全容纳任何一个关系时,以块的方式而不是以元组的方式处理关系,可以减少不少块读写次数。
算法表示如下:
for each 块B_r of r do begin
for each 块B_s of s do begin
for each 元组t_r in B_r do begin
for each 元组t_s in B_s do begin
测试元组对(t_r, t_s)是否满足连接条件
如果满足,把t_r·t_s加到结果中
end
end
end
end
假设:
——外层关系;
——内层关系;
——
中的磁盘块数(
);
——
中的磁盘块数(
)。
(a)在最坏的情况下,即缓冲区只能容纳每个关系的一个数据块(两个关系都只能有一个块在内存),
块传输次数为;磁盘搜索次数为
。(即使用
替换
)
如果内存不能使容纳任何一个关系时,则使用较小的关系作为外层关系更有效。
(b)在最好的情况下,即内存有足够的空间同时容纳每个关系的所有块,
块传输次数:;磁盘搜索次数:2。
(c)优化:外层关系可以不用磁盘块作为分块的单位,而以内存中最多能容纳的大小为单位,当然同时要留出足够的缓冲空间给内层关系及输出结果使用。也就是说,假设内存有块,则一次读取外层关系中的
块。
块传输次数:;磁盘搜索次数:
。
(3)索引嵌套循环连接
对于外层关系中的每一个元组
,可以利用索引查找
中和元组
满足连接条件的元组。
假设:
——
中的元组数;
——
中的磁盘块数(
);
——
中的磁盘块数(
);
——磁盘子系统传输一个块的数据平均消耗时间;
——磁盘块平均访问时间(磁盘搜索时间加上旋转延迟);
——使用连接条件对关系
进行单次选择操作的代价。
读取关系需要
次I/O操作,每次I/O操作需要一次磁盘搜索和一次块传输。对于关系
中的每个元组,在
上进行索引查找。这样,连接的时间代价可用
来计算。
代价计算公式表明,如果两个关系、
上均有索引时,一般把元组较少的关系作外层关系时效果较好。
(4)归并连接
归并连接算法为每个关系分配一个指针,这些指针一开始指向相应的关系的第一个元组,随着算法的执行指针遍历整个关系。归并连接算法要求每个关系对于连接属性是已经排好顺序的,而且在连接属性上具有相同值的元组集合必须能够全部存放在内存中。
算法表示如下:
C := 连接属性;
pr := r的第一个元组的地址;
ps := s的第一个元组的地址;
while (ps != null and pr != null) do begin
t_s := ps所指向的元组;
S_s := {t_s};
让ps指向关系s的下一个元组;
done := false;
while (not done and ps != null) do begin
t_s1 := ps所指向的元组;
if (t_s1[C] == t_s[C]) then begin
S_s := S_s union {t_s1};
让ps指向关系s的下一个元组;
else
done := true;
end
t_r := pr所指向的元组;
while (pr != null and t_r[C] < t_s[C]) do begin
让pr指向关系r的下一个元组;
t_r := pr所指向的元组;
end
while (pr != null and t_r[C] == t_s[C]) do begin
for each t_s in S_s do begin
将 t_s join t_r加入结果中;
end
让pr指向关系r的下一个元组;
t_r := pr所指向的元组;
end
end
假设:
——
中的磁盘块数;
——
中的磁盘块数;
——内存为每个关系(归并段)分配的缓冲块数量。
(a)一旦关系已排序,在连接属性上有相同值的元组是连续存放的。所以已排序的每一个元组只需读一次,因而每个块也只须读一次,所需磁盘块传输次数是两个文件的块数之和:,所需磁盘搜索次数为
。
(b)假设两个关系未排序,则需要先对关系和
分别进行归并排序。另假设内存大小为
个磁盘块。
由(三)可知:
对关系进行排序需要
次块传输和
次磁盘搜索,外加将结果写回时的
次块传输和
次磁盘搜索;
对关系进行排序需要
次块传输和
次磁盘搜索,外加将结果写回时的
次块传输和
次磁盘搜索;
最后,归并这两个关系需要的次块传输和
次磁盘搜索。
所以,总的块传输次数为:
总的磁盘搜索次数为:
因此,通过为每个归并段分配更多的缓冲块,磁盘搜索的次数能够显著地减少。
(五)散列连接
散列连接算法的基本思想是把这两个关系的元组划分成在连接属性值上具有相同散列值的元组集合。如下图所示:
对于以上的划分,中的元组
只需与
中的元组
相比较,而没有必要与其他任何划分里的元组
相比较。即假设
是关系
中的元组,
是关系
中的元组,
是连接属性上的散列函数,那么只有在
时
与
才须比较。若
,则
与
在连接属性上的值必不相等。然而,如果
,则必须检查
和
在连接属性上的值是否相同,因为可能实际值不同却有相同的散列值。
算法表示如下:
C := 连接属性;
/* 对关系s进行划分 */
for each元组t_s in s do begin
i := h(t_s[C]);
H_s_i := H_s_i union {t_s};
end
/* 对关系r进行划分 */
for each元组t_r in r do begin
i := h(t_r[C]);
H_r_i := H_r_i union {t_r};
end
/* 对每一划分进行连接 */
for i := 0 to n_k do begin
读H_s_i,在内存中建立其散列索引;
for each元组t_r in r_i do begin
检索s_i的散列索引,定位所有满足 t_s[C] = t_r[C] 的元组t_s;
for each匹配的元组t_s in H_s_i do begin
将 t_r join t_s 加入结果中;
end
end
end
从以上伪代码可知,该算法先为每个构造散列索引,然后用
中的元组进行探查(即在
中查找)。关系
称为构造用输入(build input),关系
称为探查用输入(probe input)。
假设:
——
中的磁盘块数;
——
中的磁盘块数;
——桶数目;
——内存大小,即内存所能容纳的磁盘块;
——内存为每个关系(归并段)分配的缓冲块数量。
(a)假设的值小于内存块数
。
关系和
的划分需要分别进行一次完整的读入与写回,该操作需要
次块传输。在构造和探查阶段每个划分杜如一次,这又需要
次块传输。由于每个划分都有可能有一个部分满的块,而这个块需写回、读入各一次,因此对于
和
而言存取这些部分满的块都至多增加
次开销。从而总的块传输次数为
。
划分总共需要次磁盘搜索。在构造和探查阶段,每个关系中
个划分中的每一个仅需一次磁盘搜索,因为每一个划分都可以顺序地读取。因此需要
次磁盘搜索。
由于的开销跟
、
相比是很小的,所以可以忽略。
(b)如果的值大于或等于内存块数
,因为没有足够的缓冲块,所以关系的划分不可能一趟完成,需要重复多趟。在每一趟中,输入的最大划分数不超过用于输出的缓冲块数。每一趟产生的存储桶在下一趟中分别被读入并再次划分,产生更小的划分(相当于构建多级索引)。这种划分方法称为递归划分。
递归划分的每一趟可将划分的大小减为原来的,不断重复操作直到每个划分最多占
块为止,那么划分关系
所需的趟数为
。
块传输次数:;
磁盘搜索次数:。
(以上笔记整理自《数据库系统概念(第6版)》)