近似函数
本页面列出了 GreptimeDB 中的近似函数,这些函数用于近似数据分析。
下述的近似函数目前仍处于实验阶段,可能会在未来的版本中发生变化。
近似去重计数 (HLL)
这里使用了 HyperLogLog (HLL) 算法来计算一组值的近似去重计数。它在内存使用和速度方面提供了高效的性能。GreptimeDB 提供了三个函数来处理 HLL 算法,具体描述如下:
由于算法的近似性质,结果可能不完全精确,但通常非常接近实际的去重计数。HyperLogLog 算法的相对标准误差约为 1.04/√m,其中 m 是算法中使用的寄存器数量。GreptimeDB 默认使用 16384 个寄存器,这使得相对标准误差约为 0.008125(即 0.8125%)。
hll
hll(value)
从给定列创建二进制的 HyperLogLog 状态。value
可以是你希望计算近似去重计数的任何列。它返回 HLL 状态的二进制表示,可以存储在表中或用于进一步计算。有关如何结合其他函数使用此函数计算近似去重计数的完整示例,请参阅 完整使用示例。
hll_merge
hll_merge(hll_state)
将多个 HyperLogLog 状态合并为一个。当你需要合并多个 HLL 计算结果时,例如聚合来自不同时间窗口或来源的数据时,这非常有用。hll_state
参数是由 hll
创建的 HLL 状态的二进制表示。合并后的状态可用于计算所有合并状态的近似去重计数。有关如何结合其他函数使用此函数计算近似去重计数的完整示例,请参阅 完整使用示例。
hll_count
hll_count(hll_state)
从 HyperLogLog 状态中计算得到近似去重计数的结果。此函数接受由 hll
创建或由 hll_merge
合并的 HLL 状态,并返回近似的去重值计数。有关如何结合其他函数使用此函数计算近似去重计数的完整示例,请参阅 完整使用示例。
完整使用示例
此示例演示了如何组合使用这些函数来计算近似的去重的用户 ID 的数量。
首先创建用于存储用户访问日志的基础表 access_log
,以及用于在 10 秒时间窗口内存储 HyperLogLog 状态的 access_log_10s
表。请注意,state
列的类型为 BINARY
,它将以二进制格式存储 HyperLogLog 状态。
CREATE TABLE access_log (
`url` STRING,
user_id BIGINT,
ts TIMESTAMP TIME INDEX,
PRIMARY KEY (`url`, `user_id`)
);
CREATE TABLE access_log_10s (
`url` STRING,
time_window timestamp time INDEX,
state BINARY,
PRIMARY KEY (`url`)
);
将一些示例数据插入到 access_log
中:
INSERT INTO access_log VALUES
("/dashboard", 1, "2025-03-04 00:00:00"),
("/dashboard", 1, "2025-03-04 00:00:01"),
("/dashboard", 2, "2025-03-04 00:00:05"),
("/dashboard", 2, "2025-03-04 00:00:10"),
("/dashboard", 2, "2025-03-04 00:00:13"),
("/dashboard", 4, "2025-03-04 00:00:15"),
("/not_found", 1, "2025-03-04 00:00:10"),
("/not_found", 3, "2025-03-04 00:00:11"),
("/not_found", 4, "2025-03-04 00:00:12");
现在我们可以使用 hll
函数为 user_id
列创建 10 秒时间窗口的 HyperLogLog 状态。输出将是 HLL 状态的二进制表示,其中包含计算后续近似去重计数所需的信息。date_bin
函数用于将数据分组到 10 秒的时间窗口中。因此,此 INSERT INTO
语句将为 access_log
表中每个 10 秒时间窗口创建 HyperLogLog 状态,并将其插入到 access_log_10s
表中:
-- 使用 10 秒窗口查询来计算 HyperLogLog 状态:
INSERT INTO
access_log_10s
SELECT
`url`,
date_bin("10s" :: INTERVAL, ts) AS time_window,
hll(`user_id`) AS state
FROM
access_log
GROUP BY
`url`,
time_window;
-- 结果类似:
-- Query OK, 3 rows affected (0.05 sec)
然后我们可以使用 hll_count
函数从 HyperLogLog 状态(即 state
列)中检索近似去重计数。例如,要获取每个 10 秒时间窗口的用户访问近似去重计数,我们可以运行以下查询:
-- 使用 `hll_count` 查询 `access_log_10s` 中的近似数据,请注意对于小型数据集,结果可能不是很准确。
SELECT `url`, `time_window`, hll_count(state) FROM access_log_10s;
-- 结果如下:
-- +------------+---------------------+---------------------------------+
-- | url | time_window | hll_count(access_log_10s.state) |
-- +------------+---------------------+---------------------------------+
-- | /dashboard | 2025-03-04 00:00:00 | 2 |
-- | /dashboard | 2025-03-04 00:00:10 | 2 |
-- | /not_found | 2025-03-04 00:00:10 | 3 |
-- +------------+---------------------+---------------------------------+
此外,我们可以通过使用 hll_merge
合并 HyperLogLog 状态,将 10 秒的数据聚合到 1 分钟级别。这使我们能够计算更大时间窗口的近似去重计数,这对于分析随时间变化的趋势非常有用。以下查询演示了如何实现:
-- 使用 `hll_merge` 合并 HyperLogLog 状态,将 10 秒的数据聚合到 1 分钟级别。
SELECT
`url`,
date_bin('1 minute' :: INTERVAL, `time_window`) AS time_window_1m,
hll_count(hll_merge(state)) as uv_per_min
FROM
access_log_10s
GROUP BY
`url`,
date_bin('1 minute' :: INTERVAL, `time_window`);
-- 结果如下:
-- +------------+---------------------+------------+
-- | url | time_window_1m | uv_per_min |
-- +------------+---------------------+------------+
-- | /dashboard | 2025-03-04 00:00:00 | 3 |
-- | /not_found | 2025-03-04 00:00:00 | 3 |
-- +------------+---------------------+------------+
请注意 hll_merge
函数如何用于合并 access_log_10s
表中的 HyperLogLog 状态,然后 hll_count
函数用于计算每个 1 分钟时间窗口的近似去重计数。如果只使用 hll_merge
而不使用 hll_count
,结果将只是合并后的 HyperLogLog 状态的不可读二进制表示,这对于分析没有太大用处。因此,我们使用 hll_count
从合并后的状态中计算得到近似去重计数。
以下流程图说明了 HyperLogLog 函数的上述用法。首先,原始事件数据按时间窗口和 URL 分组,然后使用 hll
函数为每个时间窗口和 URL 创建一个 HyperLogLog 状态,接着使用 hll_count
函数检索每个时间窗口和 URL 的近似去重计数。最后,使用 hll_merge
函数合并每个 URL 的 HyperLogLog 状态,然后再次使用 hll_count
函数检索 1 分钟时间窗口的近似去重计数。