采样
采样用于从数据集中随机选择一个子集。
示例
使用 reservoir 从 tbl 精确采样 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 支持三种采样方法:reservoir、bernoulli、system。默认情况下,指定固定行数时使用 reservoir;指定百分比时使用 system。下文将分别说明。
采样需要指定采样规模,即从总体中抽取多少元素。可使用百分比(10% 或 10 PERCENT)或固定行数(10 或 10 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
TABLESAMPLE 与 USING 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%);