本文是对论文 “Improved Correlated Sampling For Join Size Estimation” 的阅读个人总结,如有不当之处请指正。
问题是什么?
对 SQL 查询的结果行数做估算在基于代价的查询优化中是非常重要的一个步骤。以如下查询为例:
SELECT * FROM t1 JOIN t2 ON t1.a = t2.a AND t1.b = t2.b WHERE t1.c > 1 and t2.c > 1;
在考虑两张表的索引选择,以及 join 的算法选择时,需要较为准确估算查询在每个步骤后剩余的结果行数,这里的步骤主要包括选择和连接操作。相比于估算单表上的选择结果行数,准确估算多表 join 的结果行数会更困难。
目前主流的估算 join 结果行数的方法可以划分为两类:
- 基于直方图的估算方法,利用统计信息和均匀分布 / 独立性假设计算结果行数;
- 基于采样的估算方法,在小规模的独立采样数据集上执行查询的步骤,利用得到的结果行数反推真实结果行数;
基于直方图的估算方法主要有两大缺陷:
- 在 join key 的数量上扩展性不好,主要体现在两个方面:第一是随着 join key 数量和查询复杂度的增加,存储和推导直方图的代价会变得很高;第二是在多维直方图上准确估算 join 结果行数的计算公式会变得很复杂;
- 直方图主要针对的是固定前缀的范围查询估算,对于一些选择谓词的估算起不到作用,比如
t1.c like ‘%hello%’
这样的选择条件无法用直方图做出估算;
基于采样的估算方法可以规避上述基于直方图方法的两个缺陷,但也有其自己的问题:由于两张表的采样是独立的,忽略了表之间的 join 关系,导致估算准度不高。近年来,一种新的基于关联采样的 join 结果行数估算方法被提出,用来弥补简单采样策略的缺陷。
什么是关联采样?
关联采样分为线下和线上两个阶段,对于 A 和 B 两张表的连接 A ⋈ B:
- 线下阶段会首先从 A 表中以概率 p 随机抽样记录,构造出 A 的抽样
,然后计算出 B 和的 semi join 结果,即 B ⋉,再从这个 semi join 结果集中采样,构造出 B 的抽样。显而易见,中的每一条记录,都可以和中的一条或多条记录连接。
- 在线上阶段,对于带具体过滤条件的两表连接 A ⋈ B,首先在
和上应用具体的过滤条件,得到过滤后的采样和,然后对和做连接操作,得到过滤后的采样的 join 结果行数 |⋈|,最后将这个结果除以概率 p 作为真实数据集上的 join 结果行数估算。
目前业界提出了多种关联采样的变种,在一些细节步骤上做了优化,并且都证明了相比于简单采样策略的方法会有更准确的估算结果,其中被普遍认可的一个变种是「两步关联采样 CS2L」,其主要优化是精心选取了采样概率,来最小化估算的方差。但 CS2L 这种方法有如下三个缺陷:
- 当过滤后的采样集合
和很小时,估算准度很差;
- 当 join key 上数据倾斜严重时,估算准度很差;
- 计算最优的采样概率复杂度很高,并且没有公认的解决方案;
本篇论文着眼在用关联采样估算 join 结果,并解决现有关联采样方法的一些问题。主要的贡献点在于:
- 用一个模型囊括了现有的所有关联采样变种方法;
- 通过模型发现了现有变种的盲区,提出了一种新的变种 CSDL;
- 进一步发现了一个混合变种方法 CSDL-opt,在不同数据分布时用不同的采样策略;实验证明 CSDL-opt 具有最好的效果;
模型长什么样?
模型试图从 5 个维度,或者说用 5 个变量来描述一个具体的关联采样方法:
- 概率 p:如果将 A 表的 join key 上的取值去重后的集合记作
,从中采样出一个子集 V ⊆,则 p 表示中每一个值能进入 V 的概率;
- 概率 q:在构造 A 表的采样
时,对 A 表中的每一行记录,首先判断该行 join key 的值是否在 V 中,如果不在,则跳过该行记录;如果在,则该行以概率 q 进入到最终采样中;
- 概率 u:在构造 B 表的采样
时,首先计算 semi join 的结果 B ⋉,结果里的每行记录以概率 u 进入到最终采样里;
- 估算方法:即如何用根据采样算出的 join 结果行数反推真实的 join 结果行数,目前所有的关联采样变种都是用的叫 scaling up 的估算方法,即将采样算出的 join 结果除以上述的概率:
其中,
5. 是否使用 sentry:所谓的 sentry 是指在构造 S_A 和 S_B 时,在考虑概率 q 和 u 之外,保证该 join key 值的记录至少有一行被采样进 S_A 和 S_B,这样可以避免某些 join key 取值的频次较小导致无法出现在采样里;在应用了 sentry 优化后,估算公式应当调整为:
套用这个模型,现有较为知名的三种关联采样变种方法可以被描述为:
- CS2
- 对于所有的 join key 取值,p 都取 1,q 都取 θ(这里 θ 是用户预先定义的对采样集合的大小限制,具体地说,θ 是采样集合相对于原数据集比例的上限),u 都取 1,不使用 sentry,即估算是用的上述 4 中的公式;
- 行为上描述起来就是直接对 A 表用概率 θ 采样构造
,用 B ⋉整个作为;
- 这种方法的缺陷就是 B ⋉
的大小是不可预期的,一般来说都满足不了用户指定的对采样大小的 θ 限制;
- CSO
- 对于所有的 join key 取值,p 都取 θ,q 和 u 都取 1,不使用 sentry,即估算也是用的上述 4 中的公式;
- 这种方法的缺陷是对于某个 join key 取值,其在采样中的存在是 all or nothing 的状态,即要么没有一行在采样中,要么所有行都在采样中,会导致不同估算的准确度的方差很大,即如果选择条件过滤掉了不在采样中的行,join 结果的估算会比较准,而如果选择条件不能过滤掉不在采样中的行,join 结果的估算就很不准;
- CS2L
- 对于不同的 join key 取值 v,p 取不同的值;如果用
和分别表示某个 join key 取值 v 在 A 表和 B 表中出现的频次,则 p 对不同 v 取值不同且等比于 sqrt(*),即如果 v 对于 join 结果集贡献越大,将它采样进 V 的概率就越大;对于所有的 join key 值,q 和 u 取相同的小于 1 的一个值,具体的值为多少由表里的数据分布决定,如果将它当作一个变量,可以用一个包含这个变量的公式计算出不同估算的准确度的方差,以最小化这个方差为目标,可以求解出这个具体的 q 和 u 值;这个方法使用 sentry,所以其估算使用的是上述 5 中的公式;
- 这种方法的缺陷是计算出能最小化估算方差的 q 和 u 值的复杂度很高,没有一个公认的解决方案;
- 对于不同的 join key 取值 v,p 取不同的值;如果用
除了上述列举的缺陷,以上三种方法还有如下两点共同不足:
- 当采样数据集很小时,估算准确度都不高;而关联采样需要对每个 join graph(即表的连接组合方式) 中的每张表做一次采样并存储采样,这就要求采样数据集要尽可能小;
- 以上三种方法都是固定的采用一种采样策略,忽略了对数据分布特点的考虑,而论文通过实验观察到,不同数据分布下适用的最佳采样策略其实是不同的;
结合这些已有方法的缺陷,以及上述总结出来的模型,论文提出了一种新的关联采样方法 CSDL。
什么是 CSDL ?
可以用以上的模型来宏观描述 CSDL 为:
- 针对不同的数据分布特点,采用不同的 p, q 和 u 取值;
- 用一个叫做 discrete learning 的算法作为估算方法,而不是现有方法都在适用的 scaling up;discrete learning 算法可以通过很小的采样数据集准确地学习到一个离散分布,这个分布进而会被用作估算;
- 使用了 sentry 技术;
可以看到,针对上面提到的已有方法的缺陷,CSDL 都相应给出了解决方案。
Discrete Learning
首先介绍下 discrete learning 算法,该算法是用在线上阶段,根据采样
使用 discrete learning 有一个前提条件:输入的采样必须是在原数据集上通过简单随机采样得来的,即采样过程服从二项分布。在上述提到的不同采样策略中,
各种采样策略
上面提到,针对不同的数据分布,使用不同的采样策略效果会不同,即不同的 p, q 和 u 的取值策略。论文列举出了 10 种不同的 CSDL 变种,对应 10 种不同的采样策略。在这 10 种变种中,对同一个 join key 值 v,
- 对于所有的 join key 值,使用相同的 p 值和相同的 q 值;
- 对不同的 join key 值 v,
不同,但 q 使用同一个值;
- 对于所有的 join key 值,使用相同的 p 值,但使用不同的 q 值,即
对各个 v 可能不同;
- 对不同的 join key 值 v,
和都不相同;
由于 p 和 q 的取值范围是 [θ, 1],在讨论时考虑三种可能的取值,θ, 1 和 sqrt(θ),即上下界和一个中间的值。这三种取值和四种类型组合起来就形成了 10 种变种。接下来我们按类别分析上述这四类变种所使用的估算方法。
策略 1
对所有的 join key 值 v,q 的取值相同,所以
其中,N’ 表示 A 表中所有满足 join key 值在
策略 2
虽然对不同的 join key 值 v,
策略 3
由于对不同的 join key 值 v,q 的取值不同,
由于对所有的 v,q 值是相同且已知的,而采样大小的上限 θ 是给定的,因此 q 值可以被唯一确定出来的。调整后的采样
策略 4
这种情形和策略 3 本质上是一样的,也就是说,p 值对于 v 是否可变并不重要,真正有影响的是 q 值对于 v 是否可变,因为它决定了
论文对策略 ½ 和策略 ¾ 做了简单的对比讨论,提出了一个问题:为什么不直接用策略 ½ 采样,而要考虑策略 ¾?很明显,前者更简单,不需要构造虚拟采样这一步,但后者可以给那些在 A 中出现频次更高的 v 赋予更大的
哪种策略最好?
上面理论分析认为策略 ¾ 会比策略 ½ 更好,那么在实验中,究竟哪一个具体的变种才是表现最好的呢?论文在 Join Order Benchmark 上做了一些验证,发现了属于策略 3 的两个变种表现最好:
- CSDL(1, diff):p 值对所有 v 都取 1,q 和 u 对不同的 v 取不同的值;
- CSDL(θ, diff):p 值对所有 v 都取 θ,q 和 u 对不同的 v 取不同的值;
这两个变种在不同的数据分布下各有胜负,论文将影响结果的数据分布特点总结成了一个指标 join value density,其定义如下:
当 jvd 较小时,CSDL(1, diff) 表现更好;当 jvd 较大时,CSDL(θ, diff) 表现更好。这个结论从直观上很好理解,jvd 越小表明 A 表和 B 表在 join key 上值的分布越集中,因此采样时最好能将所有的 join key 值都采进 V 里,不然漏掉任何一个 join key 值可能就意味着在 join 估算中漏掉了大量的记录,所以 p 值应该取 1;相反,当 jvd 较大时,A 表和 B 表在 join key 上值的分布越分散,这时候采样中漏掉一些 join key 值影响不算大,可以将更多的采样空间留给 q 和 u,毕竟这两个参数离真实的 join 结果会更“近”一些。
论文将这两个变种结合起来,取名为 CSDL-opt。
考虑过滤条件
上述的讨论都是针对的两表等值连接,且不带过滤条件的情形,对于更一般的等值连接情形,比如像开篇给出的那个查询用例,CSDL 是怎么应用的呢?
对于线下阶段的抽样,没有任何变化,因为过滤条件一般只有在线上查询运行时才能知道具体是什么。在线上估算阶段,首先对
其中,
总结
这篇论文主要的亮点在于:
- 用一个模型描述了关联采样这个领域的所有方法;
- 利用这个模型发现了一种新的关联采样方法 CSDL
- 利用 discrete learning 作为估算方法,而不是常用的 scaling up,前者可以在小的采样集合上有很好的估算结果;
- 总结了 jvd 这个数据分布特点,在不同数据分布下找出了更好的采样策略,总结成了 CSDL-opt 方法;
参考文献
[1] G. Valiant and P. Valiant. Instance optimal learning of discrete distributions. In STOC, pages 142–155. ACM, 2016.