You need to enable JavaScript to run this app.
文档中心
云数据库 PostgreSQL 版

云数据库 PostgreSQL 版

复制全文
下载 pdf
插件
HyperLogLog 算法扩展(postgresql-hll)
复制全文
下载 pdf
HyperLogLog 算法扩展(postgresql-hll)
简介​
postgresql-hll 是基于 HyperLogLog 算法的 PostgreSQL 扩展。它引入了自定义数据类型 hll,用于以极低的存储成本和极快的计算速度解决海量数据(如亿级甚至更大规模)的基数估算(Count Distinct)问题。​
核心优势​
  • 高效的存储与性能:hll 数据对象占用的存储空间非常小且固定(通常小于 16KB),不再随原始数据量的增长而线性增加。计算速度极快,通常能在毫秒级完成。​
  • 原生的聚合能力:支持对 hll 对象进行任意维度的无损合并(Union),例如可以轻松地将每日的用户访问记录(DAU)合并为每周(WAU)或每月(MAU)的去重用户数,而无需重新扫描原始数据。​
  • 可靠的估算精度:默认配置下的标准误差约为 0.8%,能够在准确性和资源消耗之间取得良好平衡。插件还内置了稀疏(sparse)和密集(dense)两种存储模式,并能根据数据量自动切换,以优化存储效率。​
典型用途​
  • 网站/App 活跃度统计:计算日活跃用户(DAU)、月活跃用户(MAU)等核心指标。​
  • 大规模去重计数:统计海量日志中的独立 IP 数、不同商品/内容的访问用户数等。​
  • 实时数据分析:利用其高效的合并特性,进行滑动窗口分析,如计算“过去 7 天的滚动 UV”。​
注意事项​
在使用 hll 扩展前,请务必了解并评估以下关键限制,以避免在生产环境中出现意外情况。​
注意​
这是一种概率性数据结构,而非精确存储。​
  • 非精确计算​
  • hll 提供的是基数的估算值,而非精确值。虽然误差率很低,但它本质上是一种概率统计模型。因此,严禁将其用于任何要求 100% 精确的场景,例如财务记账、库存管理、订单计数等。​
  • 数据不可逆​
  • hll 对象存储的是原始数据经过哈希计算后的摘要信息,无法从中还原出任何原始值。例如,您无法从一个聚合了用户 ID 的 hll 对象中恢复出具体的 User ID 列表。它仅用于“计数”,而非“追溯”。​
  • 插件依赖强绑定​
  • 如果您的数据表中包含了 hll 类型的列,那么该表的读取和写入将强依赖于 hll 插件。如果卸载该插件(DROP EXTENSION hll;),所有 hll 列的数据将变得不可识别(显示为乱码或错误),这等同于数据丢失。​
  • 哈希函数一致性​
  • hll 对象的合并(Union)操作要求它们的哈希配置完全一致。如果您在不同系统或环境(例如,在 Java 服务端计算哈希值,在 PostgreSQL 中聚合)中使用 hll,必须确保两端使用的哈希算法与种子(Seed)完全相同。否则,生成的 hll 对象将无法合并,计算出的并集结果也将是错误的。​
  • 数据迁移与同步风险​
  • 在使用 DTS(数据传输服务)或其他工具进行数据库迁移、同步或备份恢复时,必须确保目标数据库实例已提前安装了 hll 插件。否则,在数据导入时,hll 类型的数据列会因类型无法识别而导致迁移失败或数据损坏。​
安装与卸载​
安装插件​
在需要使用 hll 功能的数据库中,执行以下 SQL 命令来安装扩展。​
​
-- 安装 hll 扩展​
CREATE EXTENSION hll;​
​
安装成功后,您可以通过查询 pg_extension 系统表来确认。​
​
-- 检查 hll 扩展是否已安装​
SELECT * FROM pg_extension WHERE extname = 'hll';​
​
如果查询有返回结果,则说明插件已成功安装并可以使用。​
卸载插件​
警告​
卸载插件会导致所有 hll 类型的数据列失效,数据将无法读取。在执行此操作前,请务必确认已没有业务依赖 hll 数据,或已完成数据迁移。​
如果您确认不再需要 hll 插件,可以执行以下命令进行卸载。​
​
-- 卸载 hll 扩展​
DROP EXTENSION hll;​
​
使用​
postgresql-hll 插件的核心工作流通常包含三个步骤:​
  1. 哈希(Hash):将需要去重计数的原始值(如用户 ID、IP 地址等)通过哈希函数转换为哈希值。​
  1. 聚合(Aggregate):将这些哈希值添加入一个 hll 数据对象中进行聚合。​
  1. 计算(Calculate):通过 hll 对象估算基数(去重数),或将多个 hll 对象合并以计算并集。​
哈希与聚合​
hll 插件不直接处理原始数据,而是处理其哈希值。最常用的方式是结合使用 hll_hash_* 系列函数和 hll_add_agg 聚合函数。​
示例:按天统计独立访客(UV)​
假设我们有一张记录用户访问行为的原始日志表 page_views:​
​
CREATE TABLE page_views (​
log_time TIMESTAMPTZ,​
user_id TEXT​
);​
​
现在,我们希望预先计算每天的 UV 数据,并存入一张结果表 daily_uniques 中。​
  1. 创建预聚合表​
  • 首先,创建一个用于存储每日 UV 数据的表,其中 users 列的类型为 hll。​
  • ​
    CREATE TABLE daily_uniques (​
    stat_date DATE PRIMARY KEY,​
    users HLL​
    );​
    ​
  1. 哈希与聚合数据​
  • 从 page_views 表中读取数据,使用 hll_hash_text 将 user_id 转换为哈希值,然后使用 hll_add_agg 按天聚合生成 hll 对象,并插入到 daily_uniques 表中。​
  • ​
    INSERT INTO daily_uniques(stat_date, users)​
    SELECT​
    DATE(log_time) AS stat_date,​
    hll_add_agg(hll_hash_text(user_id)) AS users -- 核心操作​
    FROM​
    page_views​
    GROUP BY​
    stat_date;​
    ​
说明​
hll 提供了多种哈希函数以支持不同数据类型,例如 hll_hash_bigint、hll_hash_int、hll_hash_bytea 等。请根据您的数据类型选择合适的哈希函数。​
基数估算​
当数据以 hll 类型存储后,可以使用 hll_cardinality 函数来获取其估算的基数(即去重后的数量)。​
示例:查询某一天的 UV​
​
SELECT​
stat_date,​
hll_cardinality(users) AS estimated_uv​
FROM​
daily_uniques​
WHERE​
stat_date = '2026-02-12';​
​
无损合并​
无损合并是 postgresql-hll 最强大的功能之一。您可以将存储在不同行(例如不同日期的记录)的 hll 对象进行合并,从而计算出总时间范围内的去重数,而无需重新扫描海量的原始数据。​
示例:计算月活跃用户(MAU)​
使用 hll_union_agg 函数,可以将一个月内所有天数的 hll 对象合并成一个,然后计算其总基数。​
​
SELECT​
EXTRACT(YEAR FROM stat_date) AS year,​
EXTRACT(MONTH FROM stat_date) AS month,​
-- 将该月所有天的 hll 对象合并,然后估算总基数​
hll_cardinality(hll_union_agg(users)) AS monthly_uv​
FROM​
daily_uniques​
WHERE​
stat_date >= '2026-01-01' AND stat_date < '2026-02-01'​
GROUP BY​
year, month;​
​
滑动窗口分析​
由于 hll 的合并操作性能极高,它非常适合用于计算“滚动时间窗口”内的指标,例如“过去 7 天的滚动活跃用户”。​
示例:计算 7 日滚动 UV​
通过结合窗口函数(Window Function)和 hll_union_agg,可以高效地实现滑动窗口计算。​
​
SELECT​
stat_date,​
hll_cardinality(​
hll_union_agg(users) OVER (​
ORDER BY stat_date​
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW​
)​
) AS rolling_7day_uv​
FROM​
daily_uniques​
ORDER BY​
stat_date;​
​
默认配置说明​
在创建 hll 对象时,插件会使用一套默认参数(如 log2m=11, regwidth=5 等),这套参数决定了 hll 对象的精度和存储大小。在绝大多数场景下,默认配置(误差率约为 ±0.8%)已经提供了最佳的性能和精度平衡,用户无需手动调整。​
  • hll_empty():此函数创建一个基于默认参数的空 hll 对象。​
  • hll_add_agg(...):此聚合函数同样基于默认参数来构建 hll 对象。​
警告​
具有不同参数配置的 hll 对象之间无法进行合并(Union)操作。为了确保数据的可合并性,强烈建议在整个数据库中始终使用统一的默认配置,避免在创建或聚合时手动指定不同的参数。​
常见问题与故障排查​
  • 问:为什么 hll 计算的结果和精确 COUNT(DISTINCT ...) 的结果有偏差?答: 这是正常现象。hll 是一种概率性数据结构,其设计目标是以极小的存储空间换取一个足够精确的估算值。它本身存在固有的统计误差(默认配置下约为 ±0.8%)。如果您需要 100% 的精确结果,请使用传统的 COUNT(DISTINCT ...),但需要承受其带来的巨大计算和内存开销。​
  • 问:为什么两个 hll 对象无法合并(Union)?答: 合并失败通常是因为两个 hll 对象的内部配置参数不一致。例如,一个 hll 对象在创建时指定了非默认的 log2m 值,而另一个使用了默认值。请确保所有参与合并的 hll 对象都使用完全相同的配置。最简单的做法是,始终使用 hll_empty() 和 hll_add_agg 的默认行为,不要手动指定参数。​
  • 问:为什么 hll 数据列显示为乱码或查询报错?答: 这种情况最常见的原因是当前的数据库连接中 hll 插件未安装或未启用。请执行 CREATE EXTENSION IF NOT EXISTS hll; 来确保插件可用。另一种可能是插件被意外卸载,这将导致数据无法恢复,请参见“注意事项”中的风险说明。​
  • 问:数据迁移(DTS)后,hll 列的数据为什么无法使用?答: 这通常是因为在数据导入到目标库时,目标库尚未安装 hll 插件。数据迁移工具无法识别 hll 类型,可能导致数据被错误地当作普通二进制数据导入。请务必在迁移任务开始前,在目标数据库中提前安装好 hll 插件。​
最近更新时间:2026.03.31 10:39:12
这个页面对您有帮助吗?
有用
有用
无用
无用