当您处理计数器数据时,通常假设如果计数器的值下降,则计数器已被重置。例如,如果您想计算车辆行驶的总里程数,您会期望值持续增加:1、2、3、4,依此类推。如果计数器重置为 0,您会期望这是一个新的行程,或是一辆全新的车辆。如果您想从上次中断的地方继续计数,而不是重置为 0,这可能会成为问题。如果您遇到短暂的服务器中断或任何其他原因,可能会发生重置。为了解决这个问题,您可以通过查看随时间的变化来分析计数器数据,这可以解释重置。

在 SQL 中考虑重置可能很困难,因此 Timescale 开发了聚合函数和访问器函数,以更实际的方式处理计数器的计算。

注意

计数器聚合可以在连续聚合中使用,即使它们在 PostgreSQL 中不可并行化。有关更多信息,请参阅关于并行性和排序的部分。

在此过程中,我们使用一个名为 example 的示例表,其中包含计数器数据。

  1. 创建一个名为 example 的表

    CREATE TABLE example (
    measure_id BIGINT,
    ts TIMESTAMPTZ ,
    val DOUBLE PRECISION,
    PRIMARY KEY (measure_id, ts)
    );
  2. 创建计数器聚合和 delta 访问器函数。这为您提供了计数器值在时间段内的变化,并考虑了任何重置。这允许您搜索计数器增加幅度较大或较小的十五分钟周期

    SELECT measure_id,
    delta(
    counter_agg(ts, val)
    )
    FROM example
    GROUP BY measure_id;
  3. 您还可以使用 time_bucket 函数生成一系列十五分钟增量上的 delta 值

    SELECT measure_id,
    time_bucket('15 min'::interval, ts) as bucket,
    delta(
    counter_agg(ts, val)
    )
    FROM example
    GROUP BY measure_id, time_bucket('15 min'::interval, ts);

如果您的序列不太规则,则 delta 值会受到每个十五分钟周期内样本数量的影响。您可以通过使用 extrapolated_delta 函数来改进这一点。为此,您需要提供定义外推范围的边界。在此示例中,我们使用 time_bucket_range 函数,该函数的工作方式与 time_bucket 相同,但生成桶中所有时间的开放式范围。此示例还使用 CTE 来进行计数器聚合,这使得更容易理解每个部分中发生的事情。

  1. 创建一个名为 example 的表

    CREATE TABLE example (
    measure_id BIGINT,
    ts TIMESTAMPTZ ,
    val DOUBLE PRECISION,
    PRIMARY KEY (measure_id, ts)
    );
  2. 创建计数器聚合和外推 delta 函数

    with t as (
    SELECT measure_id,
    time_bucket('15 min'::interval, ts) as bucket,
    counter_agg(ts, val, toolkit_experimental.time_bucket_range('15 min'::interval, ts))
    FROM example
    GROUP BY measure_id, time_bucket('15 min'::interval, ts))
    SELECT time_bucket,
    extrapolated_delta(counter_agg, method => 'prometheus')
    FROM t ;
注意

在此过程中,Prometheus 用于进行外推。Timescale 当前的 extrapolation 函数旨在模仿 Prometheus 项目的 increase 函数,该函数测量外推到查询区域边缘的计数器的变化。

如果将计数器聚合制作为连续聚合,它可能会更有用。

  1. 创建一个在 ts 列上分区的超表

    SELECT create_hypertable('example', by_range('ts', '15 days'::interval), migrate_data => true);
    注意

    by_range 维度构建器是 TimescaleDB 2.13 的新增功能。

  2. 创建连续聚合

    CREATE MATERIALIZED VIEW example_15
    WITH (timescaledb.continuous)
    AS SELECT measure_id,
    time_bucket('15 min'::interval, ts) as bucket,
    counter_agg(ts, val, time_bucket_range('15 min'::interval, ts))
    FROM example
    GROUP BY measure_id, time_bucket('15 min'::interval, ts);
  3. 您还可以从连续聚合重新聚合到更大的桶大小

    SELECT
    measure_id,
    time_bucket('1 day'::interval, bucket),
    delta(
    rollup(counter_agg)
    )
    FROM example_15
    GROUP BY measure_id, time_bucket('1 day'::interval, bucket);

计数器重置计算需要严格的输入排序,这意味着它们在 PostgreSQL 中不可并行化。这是因为 PostgreSQL 通过将行随机发送给工作进程来处理并行性。但是,如果您的并行性可以保证在时间上不相交的行集,则该算法可以并行化,只要它在时间范围内,并且所有行都发送到同一个工作进程。对于连续聚合和分布式超表都是这种情况,只要分区键在 group by 中,即使聚合本身在其他情况下实际上没有意义。

有关并行性和排序的更多信息,请参阅我们的开发者文档

关键词

在此页面上发现问题?报告问题 或 在 GitHub 上编辑此页