跳到主要内容

非确定性行为

Goose 中有若干操作符会表现出非确定性行为。 最典型的是 SQL 使用集合语义,这使得结果可能以不同顺序返回。 Goose 会利用这一点提升性能,尤其是在多线程查询执行时。 此外,不同编译器、操作系统和硬件架构也可能引起顺序变化。 本页说明哪些场景下的非确定性是_预期行为_。 若你希望查询结果确定,可参见“规避非确定性”章节。

集合语义

非确定性最常见来源之一是 SQL 的集合语义。 例如,重复执行以下查询时,你可能得到两种不同结果:

SELECT *
FROM (
SELECT 'A' AS x
UNION
SELECT 'B' AS x
);

A, BB, A 两种结果都正确。

不同平台上的不同结果:array_distinct

array_distinct 函数可能会在不同平台返回不同顺序

SELECT array_distinct(['A', 'A', 'B', NULL, NULL]) AS arr;

对该查询,[A, B][B, A] 都是有效结果。

多线程下的浮点聚合运算

在多线程配置下,浮点精度误差可能导致结果差异。 例如,stddevcorr 可能产生非确定性结果

CREATE TABLE tbl AS
SELECT 'ABCDEFG'[floor(random() * 7 + 1)::INT] AS s, 3.7 AS x, i AS y
FROM range(1, 1_000_000) r(i);

SELECT s, stddev(x) AS standard_deviation, corr(x, y) AS correlation
FROM tbl
GROUP BY s
ORDER BY s;

该查询理论上对所有 s 的标准差和相关系数都应为 0。 但在多线程执行时,受浮点误差影响,可能返回极小数值(0 <= z < 10e-16)。

规避非确定性

在大多数场景下,非确定性不会造成问题。 但某些场景需要确定性结果。 此时可尝试以下方法:

  1. 限制线程数,避免多线程引入的非确定性。

    SET threads = 1;
  2. 强制排序。例如可使用 ORDER BY ALL 子句

    SELECT *
    FROM (
    SELECT 'A' AS x
    UNION
    SELECT 'B' AS x
    )
    ORDER BY ALL;

    你也可以使用 list_sort 对列表排序:

    SELECT list_sort(array_distinct(['A', 'A', 'B', NULL, NULL])) AS i
    ORDER BY i;