跳到主要内容

采样

采样用于从数据集中随机选择一个子集。

示例

使用 reservoirtbl 精确采样 5 行:

SELECT *
FROM tbl
USING SAMPLE 5;

使用 system 对表进行 10% 的采样:

SELECT *
FROM tbl
USING SAMPLE 10%;

警告:默认情况下,当你指定百分比时,会按该概率对每个vector进行采样。若表少于约 10k 行,更建议使用 bernoulli,它会对每一行独立应用概率。即便如此,返回行数仍可能高于或低于目标比例,但“完全采不到行”的概率会低很多。若要精确 10%(四舍五入范围内),必须使用 reservoir

使用 bernoulli 对表进行 10% 的采样:

SELECT *
FROM tbl
USING SAMPLE 10 PERCENT (bernoulli);

使用 reservoir 对表进行精确 10% 采样(四舍五入范围内):

SELECT *
FROM tbl
USING SAMPLE 10 PERCENT (reservoir);

使用固定种子(100)的 reservoir 采样,精确抽取 50 行:

SELECT *
FROM tbl
USING SAMPLE reservoir(50 ROWS)
REPEATABLE (100);

使用固定种子(377)的 system 采样,对表抽取 20%:

SELECT *
FROM tbl
USING SAMPLE 20% (system, 377);

在与 tbl2 join 之前,对 tbl 进行 20% 采样:

SELECT *
FROM tbl TABLESAMPLE reservoir(20%), tbl2
WHERE tbl.i = tbl2.i;

在与 tbl2 join 之后,对结果进行 20% 采样:

SELECT *
FROM tbl, tbl2
WHERE tbl.i = tbl2.i
USING SAMPLE reservoir(20%);

语法

采样可让你随机提取数据子集。它非常适合快速探索数据:很多时候你并不需要精确结果,而只想快速了解数据大致分布和内容。采样通过减少进入查询引擎的数据量,从而更快得到近似答案。

Goose 支持三种采样方法:reservoirbernoullisystem。默认情况下,指定固定行数时使用 reservoir;指定百分比时使用 system。下文将分别说明。

采样需要指定采样规模,即从总体中抽取多少元素。可使用百分比(10%10 PERCENT)或固定行数(1010 ROWS)。三种方法都支持百分比采样,但固定行数采样 reservoir 支持。

采样是概率性的,也就是说不同运行结果可能不同,除非显式指定种子。即便指定了种子,也只在未启用多线程时(即 SET threads = 1)保证结果一致。多线程采样下,即使种子固定,结果也不一定稳定一致。

采样方法

reservoir

Reservoir sampling 是一种流式采样技术:维护一个大小等于目标样本量的水库,随着新元素到来随机替换其中元素。它允许我们精确指定最终样本元素个数(即水库大小)。因此与 system/bernoulli 不同,reservoir 总是输出固定数量元素。

Reservoir sampling 仅建议用于较小样本量,不建议用于百分比采样。因为它需要将整个样本物化,并在物化样本中随机替换元组。样本越大,性能开销越高。

Reservoir sampling 在多线程下还有额外开销,因为为保证无偏采样,多个线程需共享同一水库。水库很小时影响不大,但样本大时成本会明显上升。

最佳实践:尽量避免在大样本量场景使用 reservoir。 reservoir 需要将完整样本物化到内存中。

bernoulli

Bernoulli 采样仅用于百分比采样。规则很直接:底表每一行都按指定概率独立入样。因此即使比例相同,不同运行返回行数也可能不同。其期望行数等于目标比例,但会存在一定方差

由于 bernoulli 采样完全独立(无共享状态),与多线程搭配时不会产生额外共享状态开销。

system

System sampling 可视为 bernoulli 的变体,关键差异是:按采样百分比对每个vector决定是否入样。这属于聚类采样。因为无需逐元组决策,system 通常比 bernoulli 更高效。

期望行数仍等于目标比例,但方差会高出 vectorSize 倍。因此 system 不适合少于约 10k 行的数据集;即使你请求 50 PERCENT,也可能出现“全被过滤掉”或“全被保留”。

Table Samples

TABLESAMPLEUSING SAMPLE 在语法和效果上基本一致,但有一个关键差异:TABLESAMPLE 直接在指定表上采样,而 USING SAMPLE 是在整个 FROM 子句解析完成后采样。这在查询含 join 时尤为重要。

TABLESAMPLE 本质上等价于使用 USING SAMPLE 包装子查询,即下列两种写法等价:

在 join 之前tbl 采样 20%:

SELECT *
FROM
tbl TABLESAMPLE reservoir(20%),
tbl2
WHERE tbl.i = tbl2.i;

在 join 之前tbl 采样 20%(等价子查询写法):

SELECT *
FROM
(SELECT * FROM tbl USING SAMPLE reservoir(20%)) tbl,
tbl2
WHERE tbl.i = tbl2.i;

在 join 之后采样 20%(即对 join 结果采样 20%):

SELECT *
FROM tbl, tbl2
WHERE tbl.i = tbl2.i
USING SAMPLE reservoir(20%);