聚合数据

PostgreSQL 数据聚合的工作原理

Lots of neon squares over a black background representing data aggregation.

作者:Juan José Gouvêa

PostgreSQL 支持一些强大的数据聚合方法。但究竟是什么让 PostgreSQL 的聚合功能如此有效,以及它们在底层是如何运作的?

在本文中,我们将深入探讨 PostgreSQL 的数据聚合功能。我们将探索这些功能的工作原理、它们在不同场景中的优势以及使 PostgreSQL 能够高效处理复杂聚合任务的技术细节。 

无论您是数据库管理员、开发人员还是数据爱好者,了解 PostgreSQL 的聚合方法都将增强您有效操作和分析数据的能力。让我们一起踏上这段旅程吧。

PostgreSQL 数据聚合基础

让我们从 PostgreSQL 聚合函数 开始,它们旨在从一组输入值中计算单个结果。这些函数对于以各种形式汇总和分析数据至关重要。它们的主要特征是能够作用于一组行并返回单个聚合结果。

内置聚合函数

PostgreSQL 支持多种类型的内置聚合函数

1. 通用聚合函数:包括 AVGCOUNTMAXMINSUM 等函数,常用于基本统计运算。

2. 统计聚合函数:针对更复杂的统计分析,这些函数包括 stddevvariancecorr(相关系数)以及各种回归函数。  

3. 有序集聚合函数:percentile_cont percentile_disc 等函数属于此类。它们用于计算有序统计,通常涉及 百分位运算

4. 假设集聚合函数:rankdense_rank 等函数属于此类。它们与 窗口函数 相关,用于假设数据场景。

5. 分组操作:GROUPING 等函数与分组集一起使用,以区分复杂分组场景中的结果行。  

自定义聚合函数

除了内置函数之外,PostgreSQL 还允许用户创建针对特定需求的自定义聚合函数。这种灵活性使得能够处理默认函数集未涵盖的独特数据聚合场景,这对于高效数据操作和分析至关重要。

PostgreSQL 数据聚合的机制

数据聚合的机制涉及一个过程,其中聚合函数根据一组行计算结果,并在遇到新行时更新内部状态。这个过程是 Postgres 中数据聚合的基础,对于高效的数据分析和查询至关重要。

使用状态转换函数对值进行求和

状态和转换函数

聚合的状态:PostgreSQL 中的每个聚合函数都维护一个内部状态,反映它所遇到的数据。例如,MAX() 函数只跟踪遇到的最大值。

状态转换函数:这是数据聚合过程中一个至关重要的组成部分。它在处理新行时更新聚合函数的内部状态。该函数接受当前状态和来自传入行的值,将它们组合起来形成一个新的状态。它可以用 next_state = transition_func(current_state, current_value)​ 表示。  

复杂状态管理

然而,并非所有聚合都有像 MAX() 那样简单的状态。一些聚合,例如 AVG(),需要更复杂的状态。例如,为了计算平均值,PostgreSQL 会存储遇到的值的总和和计数。这个复杂状态会随着每个新行的处理而更新,最终的平均值通过将总和除以计数来计算。​​

最终函数

在处理完所有行后,将对状态应用一个最终函数以生成结果。这个函数接受最终状态(即在处理完所有行后转换函数的输出),并执行必要的计算以生成最终的聚合结果。它可以用 result = final_func(final_state)​​ 表示。

数据聚合的更广泛背景

了解这些机制至关重要,尤其是在处理大型数据集时。数据聚合使能够将详细的原子数据行(通常从多个来源收集)汇总成总计或汇总统计数据。这不仅为业务分析和统计分析提供了宝贵的见解,而且还极大地提高了大型数据集查询的效率。聚合数据可以代表大量原子数据,使其更易于管理和访问。​​

开发人员如何优化 PostgreSQL 数据聚合函数

优化 PostgreSQL 数据聚合函数,尤其是在处理大量数据时,对于高效的数据处理和更快的查询响应至关重要。让我们探索一些有效的方法。

利用物化视图

PostgreSQL 中的物化视图缓存聚合数据,与实时计算相比,可以实现更快的查询响应。但是,这些视图需要在数据更新后刷新,这可能会占用大量资源。为了缓解这个问题,开发者可以:

1. 缓存聚合:在物化视图中缓存结果,查询此缓存有助于减少计算时间。

2. 实施缓存失效策略:这对那些不需要秒级新鲜度的數據至关重要。

3. 预先聚合数据:在单独的表中预先聚合数据,并通过触发器更新它,可以显着提高性能。

两步聚合

您可以利用其他策略来优化 PostgreSQL 中的数据聚合,我们当然已经使用过这些策略。例如,开发人员可以使用两步聚合过程来模拟 PostgreSQL 的转换/最终聚合函数实现 - 检查我们使用 date_bin() 函数的以下示例。此方法包括对数据进行分组,然后对这些分组应用聚合函数。此方法对于时间序列数据特别有用(这导致我们在整个 超函数 中采用它)。

使用 date_bin() 函数

date_bin() 函数 是 PostgreSQL 如何处理时间序列数据聚合的一个例子。它允许将数据分组到时间桶中,例如按天对每月数据进行分组。通过在固定时间间隔(如 24 小时)内进行聚合,计算速度变得更快,这对高密度数据来说意义重大。

示例

-- Grouping monthly data by day
SELECT date_bin('1 day', time, '2023-01-01') as day, AVG(value)
FROM measurements
GROUP BY day;

此查询按天对一个月内的數據进行分组,并计算每一天的平均值。只要一个桶中的数据是稳定的,它就可以与缓存的聚合一起使用。

PostgreSQL 数据聚合的挑战

但并非一切都一帆风顺 - 尽管 PostgreSQL 具有数据聚合功能,但它可能会面临一些挑战,影响这些操作的效率和有效性。以下是一些挑战:

优化和去重限制

PostgreSQL 在某些情况下可能难以优化或去重数据。当处理大型数据集或复杂查询时,这种限制变得很明显,PostgreSQL 可能无法有效地处理冗余数据或按预期优化查询。例如,在涉及大量联接或子查询的场景中,PostgreSQL 可能无法有效地去重数据,从而导致资源使用增加和性能下降。

重新聚合歧义

另一个挑战是重新聚合跨不同时间间隔的数据的歧义。例如,可能不清楚某些聚合函数是否可以重新应用于按分钟间隔而不是按天间隔聚合的数据。您需要了解这些聚合函数的内部工作原理,才能确定它们在不同环境中的适用性。但是,对这种深入技术知识的需求对于某些用户来说可能是一个障碍,尤其是 PostgreSQL 新手。

date_bin() 函数的限制

如前所述,PostgreSQL 中的 date_bin() 函数对于时间序列数据聚合可能很有用,但它有一些限制。具体来说,它只能对小于一个月的间隔进行分桶。这种限制意味着,对于跨越几个月甚至几年的长期数据分析,date_bin() 无法利用其分桶效率。

这就是为什么您需要找到用于在更长时间段内聚合数据的替代方法或解决方法的原因。而这就是连续聚合发挥作用的地方。🙂

连续聚合和 time_bucket()

在 Timescale,我们发现了一种更有效的方法来加速大型数据集上的查询,并绕过 Postgres 物化视图的限制: 连续聚合。这些聚合是物化视图的扩展,在后台增量并自动刷新查询。这意味着只有更改的数据才会重新计算,而不是整个数据集,从而显着提高性能。此外,它们允许即使是更大的数据集也具有逐秒的聚合。

因此,总而言之,以下是连续聚合的一些功能

它们会自动更新:它们会不断刷新新数据插入和更新的物化,使其比传统物化视图更有效率。

它们使用刷新策略:您可以定义一个策略来指定连续聚合视图应该以多高的频率更新,包括最新数据。

它们可以使用 WITH NO DATA 创建:此选项避免在创建时对整个基础数据集进行聚合,从而提高效率。

它们允许您自定义刷新计划:您可以根据您的用例调整刷新策略,考虑诸如准确性要求和数据摄取工作负载等因素。

time_bucket() 函数:灵活的时间间隔

time_bucket() 函数是 PostgreSQL 的 date_bin() 函数的扩展,您可以在 TimescaleDB 中使用它。虽然它类似于 date_bin(),但它将为您提供更大的桶大小和开始时间灵活性。

它的功能包括任意时间间隔,它使能够对各种时间间隔内的數據进行分组。这提供了一个灵活的工具,用于聚合时间序列数据,通常与 GROUP BY 一起使用以进行聚合计算。

time_bucket() 的示例用法

  -- Calculating average daily temperature
  SELECT time_bucket('1 day', time) AS bucket,
    avg(temperature) AS avg_temp
  FROM weather_conditions
  GROUP BY bucket
  ORDER BY bucket ASC;

此代码段显示了如何使用 time_bucket() 从数据集中计算每日平均温度。默认情况下,time_bucket() 显示桶的开始时间。但是,用户可以通过对 time 列应用数学运算来将其更改为显示桶的结束时间。

offset 参数在 time_bucket() 中允许调整桶所跨越的时间范围。此功能使用户能够将桶的开始时间和结束时间向后或向前移动,从而在数据分析中提供额外的灵活性。

date_bin() 不同,time_bucket() 可以将数据分桶到多个月甚至年的间隔中。这使得它适合于长期数据分析,并能够在较长时间段内有效地进行分桶。

-- Example: Using time_bucket() for weekly data aggregation
SELECT time_bucket('1 week', time) AS week,
       AVG(measurement)
FROM data_table
GROUP BY week;

连续聚合与 time_bucket() 的集成

正如您可能已经意识到的那样,将连续聚合与 time_bucket() 的灵活性相结合,赋予了 TimescaleDB 强大的功能

聚合中的高压缩率:在连续聚合中使用 time_bucket() 允许实现高压缩率,这在处理大量时间序列数据和其他大型数据集时尤其有利。

跨各种时间段的聚合:这种组合允许用户检查跨任何时间段的聚合,从短间隔到多年趋势。

高效的实时监控:time_bucket() 驱动的连续聚合,可以促进对聚合的实时监控。即使在更新旧数据时,它们也能保持速度和效率,确保对时间序列数据的分析查询保持快速和可靠。查看有关 Postgres 中的实时分析 的这篇文章以了解更多信息。

  

下一步

现在您已经了解了有关 PostgreSQL 数据聚合的一些主要思想,我们希望您能够更好地利用它来处理大型数据集。

如果您想充分利用您的数据 - 无论其大小 - 使用 Timescale 及其功能,例如连续聚合和 time_bucket() 函数,是快速高效地管理和分析数据的最佳选择。我们推荐这篇关于 了解 PostgreSQL 聚合和超函数的设计 的详细说明,以加深您的理解,并探索更多高级功能。