Quick start

Updated at:

Ganos TSDB is a time-series database plug-in built on PolarDB for PostgreSQL as an extension. It inherits all capabilities of a PolarDB for PostgreSQL cluster, including shared storage, one primary node with multiple read-only nodes, and backup and recovery. In addition, it is fully compatible with the open-source TimescaleDB Apache 2.0 edition and provides advanced time-series features such as continuous aggregation, time-series compression, and statistical analysis. This topic describes the basic concepts of the time-series database and the basic usage of Ganos TSDB, including how to enable and upgrade it, as well as how to use the time-series database.

Scope of application

  • Supported PolarDB for PostgreSQL versions:

    • PostgreSQL 14 (minor engine version 2.0.14.13.26.0 or later).

    • PostgreSQL 16 (minor engine version 2.0.16.9.8.0 or later).

  • Serverless clusters are not supported. Use a Subscription or Pay-as-you-go cluster instead.

Note

You can view the minor engine version in the console or by running the SHOW polardb_version; statement. If the minor engine version does not meet the requirements, upgrade the minor engine version。

Basic concepts

  • Time series data: A sequence of data points recorded in chronological order. Each data point contains not only a numeric value but also an associated timestamp, reflecting the trend or pattern of a variable over time. Time series data is widely used in various fields, such as financial transactions, meteorological observations, sensor monitoring, website traffic analysis, and epidemic studies. Key elements of time series data include:

    • Timestamp: Each data point has a clear time marker indicating when the data was collected.

    • Observed value: The data value measured or recorded at each time point.

    • Sequential order: Data points are arranged in chronological order, reflecting the natural temporal relationship between data points and giving time series data its time-series characteristics.

    • Trends and periodicity: Time series data analysis often focuses on long-term trends, seasonal fluctuations, cyclical changes, and random variations in the data.

  • Metrics: Monitoring indicators for time series data, such as temperature, stock prices, and network traffic — any quantifiable measurement. Metrics help understand behavior patterns of time series data, predict future trends, and detect and respond to anomalies in a timely manner.

  • Aggregation: The process of consolidating higher-frequency data into lower-frequency data in time series analysis. In simple terms, data is merged or summarized along the time dimension to reduce granularity while retaining key trends or features. This approach is useful for data reduction, trend analysis, anomaly detection, and predictive model building.

  • Downsampling: A common operation in time series data processing that reduces the sampling frequency of time series data — that is, reduces the number of data points while attempting to retain the key features or trends of the original data. Common downsampling methods include:

    • Decimation: The most direct method, simply taking every Nth data point. For example, if the original sequence is sampled 10 times per second, you can keep only the first of every 5 points, achieving a sampling rate of 2 times per second. This method is simple but may lose high-frequency information.

    • Averaging: Before downsampling, consecutive data points are averaged. For example, the average of 5 consecutive points becomes a single new data point. This method helps smooth data and reduce noise, but may obscure rapid changes.

    • Median: Similar to averaging, but uses the median of consecutive data points instead of the mean. This is more robust when processing time series that contain outliers.

    • Max/Min: Selects the maximum or minimum value from consecutive data points as the new data point, suitable for scenarios where peak or valley values must be preserved.

    • Resampling: Adjusts data to new time points through interpolation or other methods, then performs uniform sampling on the new timeline. Common resampling methods include linear interpolation, nearest neighbor interpolation, and cubic interpolation. Resampling methods are more flexible and can adjust to any sampling frequency while preserving the data distribution as much as possible.

    • Model-based downsampling: Uses statistical or machine learning models to predict data points at lower sampling rates, such as using ARIMA or LSTM models to predict values at each new time point, and then uses these predicted values as the downsampled data. This method enables more intelligent downsampling but has higher computational costs.

Ganos TSDB vs. TimescaleDB

  • TimescaleDB: An open-source time-series database built on PostgreSQL, optimized for handling time-series data such as sensor data, monitoring metrics, and financial data. Its design goal is to provide efficient storage, fast writes, and complex query capabilities for time-series data while retaining full PostgreSQL SQL functionality.

  • Ganos TSDB: A time-series engine developed on top of the open-source TimescaleDB, running on PolarDB for PostgreSQL clusters as a plug-in. It fully inherits all PolarDB capabilities such as shared storage, one primary node with multiple read-only nodes, and backup and recovery.

Differences

The two differ primarily in the features they provide. TimescaleDB Apache 2 Edition is the version of TimescaleDB released under the Apache 2.0 license. The Apache 2.0 open-source license allows anyone to use the code and provide it as a service, but only basic features are included.

In contrast, Ganos TSDB is fully compatible with TimescaleDB and additionally provides advanced features such as time-series Continuous aggregates, data compression, and OSS tiered storage for hot and cold data.

Enable the time-series database

To enable the time-series database capability in a PolarDB for PostgreSQL cluster, add timescaledb to the shared_preload_libraries parameter. You can modify the shared_preload_libraries parameter in the console.

Note

Modifying the shared_preload_libraries parameter restarts the cluster. Plan your maintenance window before making this change.

Create the plug-in

The Ganos TSDB plug-in depends on TimescaleDB. You can create the extensions using either of the following methods.

  • Create ganos_tsdb with CASCADE, which also creates the TimescaleDB extension.

    CREATE EXTENSION ganos_tsdb CASCADE;
  • Create the extensions manually. Create TimescaleDB first, then create ganos_tsdb.

    CREATE EXTENSION timescaledb;
    CREATE EXTENSION ganos_tsdb;
Note
  • If you encounter an error such as ERROR: Disable the injection of custom functions when creating extension: metadata_insert_trigger (21128) when installing the plug-in, Contact us to enable the required permissions before running the installation command.

  • To avoid permission issues, we recommend that you install the extension in the PUBLIC schema.

    CREATE EXTENSION ganos_tsdb WITH SCHEMA PUBLIC CASCADE;

Upgrade the plug-in

For clusters that already have Ganos TSDB or TimescaleDB installed, you can upgrade by running the following commands:

-- Upgrade TimescaleDB first
ALTER EXTENSION timescaledb UPDATE;

-- Then upgrade ganos_tsdb
ALTER EXTENSION ganos_tsdb UPDATE;

Hypertables

A hypertable is a special type of table provided by the time-series database that can easily handle time-series data. All operations that can be performed on regular tables can also be performed on hypertables.

Features

  • Hypertables automatically partition data by time. You interact with hypertables the same way as regular tables, but hypertables also have additional features that make it easier to manage time-series data.

  • In the time-series database, hypertables coexist with regular tables. Use hypertables to store time-series data to improve write and query performance, and to support sequence functions on hypertables.

  • With hypertables, the time-series database can partition time-series data based on time parameters. In the background, the database automatically sets up and maintains hypertable partitions.

Create a hypertable

The syntax for converting a regular table to a hypertable is as follows:

SELECT create_hypertable(
    'table_name',
    'time_column_name',
    'partitioning_column_name',
    number_partitions,
    'associated_schema_name',
    'associated_table_prefix',
    chunk_time_interval,
    create_default_indexes,
    if_not_exists,
    partitioning_func,
    migrate_data,
    chunk_target_size,
    chunk_sizing_func,
    time_partitioning_func
);

The following table describes the parameters.

Parameter

Required

Description

table_name

Yes

The name or OID of the table to be converted to a hypertable. The hypertable keeps the same name after conversion.

time_column_name

Yes

The name of the time column in the source table. The following data types are supported:

  • TIMESTAMP/TIMESTAMPTZ/DATE.

  • Integer types, interpreted as microseconds.

  • INTERVAL.

partitioning_column_name

No

The partitioning column name. By default, the table is partitioned by the time column.

number_partitions

No

The number of partitions.

associated_schema_name

No

The schema where the hypertable resides.

associated_table_prefix

No

Since a hypertable consists of a series of chunk tables that are automatically created internally, you can specify the prefix for chunk tables here.

chunk_time_interval

No

The time interval for hypertable chunks. Default is 7 days.

create_default_indexes

No

Whether to create a B-tree index on the time column of the hypertable. Valid values:

  • true (default): Creates a B-tree index on the time column.

  • false: Does not create a B-tree index on the time column.

if_not_exists

Whether to report an error if the hypertable already exists. Valid values:

Whether to report an error if the hypertable already exists. Valid values:

  • true: Reports an error if the hypertable already exists.

  • false (default): Does not report an error if the hypertable already exists.

partitioning_func

No

If spatial partitioning is used, you can specify a custom spatial partitioning function.

migrate_data

No

Whether to migrate existing data from the source table to the hypertable. Valid values:

  • true: Migrates existing data to the hypertable.

  • false (default): Does not migrate existing data to the hypertable.

chunk_target_size

No

The target size for chunks (example: '1000MB', 'estimate', or 'off').

chunk_sizing_func

No

Used with chunk_target_size to specify a custom function to calculate the chunk time interval and apply it to new chunks.

time_partitioning_func

No

The partitioning function for time-based partitioning.

Hypertable partitioning

  • When you create and use a hypertable, it automatically partitions data by time. Data can also be partitioned by space.

  • Each hypertable consists of child tables called chunks. Each chunk is assigned a time range and contains only data within that range. If the hypertable is also partitioned by space, each chunk is also assigned a subset of space values.

  • Each chunk of a hypertable stores data only within a specific time range. When data is inserted for a time range that does not yet have a chunk, a chunk is automatically created to store it.

By default, the chunk time interval is 7 days. You can use the following SQL statement to change the partition time interval.

SELECT set_chunk_time_interval(
    'table_name',
    chunk_time_interval,
    'dimension_name'
);

The following table describes the parameters.

Parameter

Description

table_name

The name of the hypertable.

chunk_time_interval

The chunk time interval for the hypertable.

dimension_name

Optional. The partitioning (dimension) strategy. Default is NULL.

Example

The following example uses transaction data to create a time-series hypertable:

  1. Create a regular table:

    CREATE TABLE transaction_data(
       tm TIMESTAMPTZ NOT NULL,
       id INT NOT NULL,
       price double precision);
  2. Set the replica identity of the regular table to DEFAULT mode:

    ALTER TABLE transaction_data replica IDENTITY DEFAULT;
  3. Convert the regular table to a hypertable:

    SELECT create_hypertable('transaction_data', 'tm', chunk_time_interval => INTERVAL '1 day');
    Note

    If the table already contains data, add the migrate_data parameter and set it to true when converting to a hypertable. However, if the table contains a large amount of data, the conversion may take a long time.

  4. (Optional) Change the hypertable partition interval.

    SELECT set_chunk_time_interval('transaction_data', chunk_time_interval => INTERVAL '2 day');

Continuous aggregates

Continuous aggregates are an automated precomputation mechanism that periodically or in real time precompute predefined aggregation results such as hourly averages, converting complex queries into fast reads of precomputed results. They are similar to materialized views but optimized for time-series data, supporting incremental updates and avoiding repeated computation of historical data to accelerate queries on very large datasets.

Benefits

  • Query acceleration: Query precomputed results directly to avoid full table scans of raw data.

  • Resource savings: Reduce CPU and memory consumption for real-time computation.

  • Automated maintenance: Automatically handle aggregation updates for new and old data without manual triggering.

Create a continuous aggregate

CREATE MATERIALIZED VIEW transaction_min_cagg
    WITH (timescaledb.continuous) -- Declare as a continuous aggregate
    AS
    SELECT id,
        time_bucket(INTERVAL '1 min', tm) AS bucket, -- Aggregate by 1-minute intervals
        AVG(price),
        MAX(price),
        MIN(price)
    FROM transaction_data
    GROUP BY id, bucket;

Continuous aggregate data storage

Continuous aggregate results are stored in independent materialized views. Data is stored in time-based chunks that are aligned with the underlying hypertable partitions.

Job scheduling

Continuous aggregates use job scheduling to write and refresh data. The job scheduling feature enables automatic or manual refresh of aggregation results.

Create a job

Create a job that can automate the scheduling of a function or stored procedure.

integer add_job(
                proc REGPROC,
                schedule_interval INTERVAL,
                config JSONB DEFAULT NULL,
                initial_start TIMESTAMPTZ DEFAULT NULL,
                scheduled BOOL DEFAULT true,
                check_config REGPROC DEFAULT NULL,
                fixed_schedule BOOL DEFAULT TRUE,
                timezone TEXT DEFAULT NULL
                );

The following table describes the parameters.

Parameter

Description

proc

The name or OID of the function or stored procedure to execute.

schedule_interval

The interval between job executions.

config

Job configuration parameters.

initial_start

The start time for the job. Default is the current time.

scheduled

Whether to run automatically. Default is true, which means the job runs automatically.

check_config

A function to validate whether the config parameter is valid. Default is NULL.

fixed_schedule

Whether to run the job at fixed time intervals. Default is true.

timezone

The time zone. Default is NULL.

Example
  1. Prepare the base data. Create the stored procedure to be executed.

    CREATE OR REPLACE PROCEDURE user_defined_action(job_id int, config jsonb) LANGUAGE PLPGSQL AS
    $$
    BEGIN
      RAISE NOTICE 'Executing action % with config %', job_id, config;
    END
    $$;
  2. Create a job. The function returns the job id.

    SELECT add_job('user_defined_action','1 hour');
    SELECT add_job('user_defined_action','1h', initial_start => '2024-03-11 00:00:00');

Modify a job

Modify a job that has already been created.

void alter_job(    job_id INTEGER,
        schedule_interval INTERVAL = NULL,
        max_runtime INTERVAL = NULL,
        max_retries INTEGER = NULL,
        retry_period INTERVAL = NULL,
        scheduled BOOL = NULL,
        config JSONB = NULL,
        next_start TIMESTAMPTZ = NULL,
        if_exists BOOL = FALSE,
        check_config REGPROC = NULL,
        fixed_schedule BOOL = NULL,
        initial_start TIMESTAMPTZ = NULL,
        timezone TEXT DEFAULT NULL
        );

The following table describes some of the parameters.

Note

This section describes only some parameters. For the full parameter list, see Create a job parameters.

Parameter

Description

job_id

The id of the job to modify.

max_runtime

The maximum runtime for the job. Default is NULL.

max_retries

The maximum number of retries after job failure. Default is NULL.

retry_period

The retry interval after job failure. Default is NULL.

Example
SELECT alter_job(1002, schedule_interval => INTERVAL '2 hours');

Run a job manually

Manually run a job that already exists.

void run_job(job_id int);

The following table describes the parameters.

Parameter

Description

job_id

The id of the job to run.

Example
CALL run_job(1002);

Delete a job

Delete a job.

void delete_job(job_id int);

The following table describes the parameters.

Parameter

Description

job_id

The id of the job to delete.

Example
SELECT delete_job(1002);

Refresh continuous aggregates

Refresh manually

The stored procedure for manually refreshing a continuous aggregate.

refresh_continuous_aggregate(
    cagg     REGCLASS,
    window_start             "any",
    window_end               "any"
    );

The following table describes the parameters.

Parameter

Description

cagg

The name or OID of the continuous aggregate to refresh.

window_start

The start time of the refresh window. The type must match the continuous aggregate time column.

  • If the refresh window start time is not aligned to a unit boundary, use date_trunc. For example, for a continuous aggregate by minute with a refresh window time of 2024-03-11 13:01:29, set the refresh window start time to 2024-03-11 13:01:00.

  • We recommend that you do not set this value to NULL. When set to NULL, the database retrieves the start time from the table. If the table contains a large amount of data, the refresh may take a long time to complete.

window_end

The end time of the refresh window. The type must match the continuous aggregate time column.

If the refresh end time is unclear, set it to NULL to refresh up to the latest data in the table.

Usage notes

  • The refresh window time must match the data time in the table. When refreshing a continuous aggregate, only data in fully matching time windows is refreshed. Aggregation cannot be computed for incomplete time periods.

  • If multiple calls to refresh_continuous_aggregate have overlapping time windows, only time periods with new or modified data are aggregated. Time periods without data modifications are not processed.

  • refresh_continuous_aggregate is a stored procedure function and must be called using CALL.

Example

CALL refresh_continuous_aggregate('transaction_min_cagg', '2024-03-11 00:00:00+08'::timestamptz, NULL);

Refresh automatically

The following two methods are available for automatic refresh of continuous aggregates.

(Recommended) Combine withJob scheduling

Use add_job to implement automatic refresh tasks for greater flexibility. Note the difference between the job start time initial_start and the refresh window start time window_start. The job start time must be aligned to an exact hour. If set to NULL, the job runs immediately. If set to a future time, the job starts at the specified time.

Example

  1. Define the refresh policy.

    CREATE OR REPLACE PROCEDURE transaction_min_cagg_refresh() LANGUAGE PLPGSQL AS
    $$
    DECLARE
    BEGIN
        -- Refresh window starts at '2024-03-11 00:00:00+08', window_end is NULL to refresh up to the latest data
        CALL refresh_continuous_aggregate('transaction_min_cagg', '2024-03-11 00:00:00+08'::timestamptz, NULL);
    END
    $$;
  2. Add the refresh job to the automatic scheduling queue. In this example, the job runs every minute, starting at '2024-04-01 00:00:00+08'.

    SELECT add_job('transaction_min_cagg_refresh','1 min', initial_start => '2024-04-01 00:00:00+08'::timestamptz);

Use the add_continuous_aggregate_policy function

Create a refresh policy for a specified continuous aggregate to trigger periodic automatic refreshes.

integer add_continuous_aggregate_policy(
                cagg REGCLASS,
                start_offset "any",
                end_offset "any",
                schedule_interval INTERVAL,
                if_not_exists BOOL = false,
                initial_start TIMESTAMPTZ = NULL,
                timezone TEXT = NULL
                );

The following table describes the parameters.

Parameter

Description

cagg

The name or OID of the continuous aggregate to refresh.

start_offset

The offset from the function execution time that defines the start of the refresh window. If set to NULL, the refresh window starts at the earliest data time in the hypertable.

The start_offset value must be greater than the end_offset value.

end_offset

The offset from the function execution time that defines the end of the refresh window. If set to NULL, the refresh window ends at the latest data time in the hypertable.

schedule_interval

The refresh interval. Default is 1 day.

if_not_exists

Whether to report an error if the refresh policy already exists. Valid values:

  • true: If the refresh policy already exists, a warning is issued instead of an error.

  • false (default): If the refresh policy already exists, an error is reported.

initial_start

The first execution time of the function. Default is NULL. If specified, it works with the schedule_interval parameter.

time_zone

The time zone. Default is NULL, which means UTC time is used.

Example:

SELECT add_continuous_aggregate_policy('transaction_min_cagg',
    start_offset => '1 hour', -- Process data from 1 hour ago (to ensure data completeness)
    end_offset => INTERVAL '0', -- Process up to the current time
    schedule_interval => INTERVAL '1 sec' -- Refresh every second
    );

Real-time queries

When querying continuous aggregates, if real-time query is not enabled, only data with completed aggregations is accessible. You can enable real-time query to access newly ingested data that has not yet been aggregated.

  • Enable real-time query

    ALTER MATERIALIZED VIEW transaction_min_cagg SET (timescaledb.materialized_only = false);
  • Disable real-time query

    ALTER MATERIALIZED VIEW transaction_min_cagg SET (timescaledb.materialized_only = true);

Nested aggregates

Nested aggregates refer to creating continuous aggregates on top of materialized views generated by other continuous aggregates. For example, you can create hourly aggregates on top of per-minute aggregates, and monthly aggregates on top of daily aggregates. This reduces data redundancy and improves aggregation performance.

  1. Create the first-level aggregate. In this example, a per-minute aggregate is created.

    CREATE MATERIALIZED VIEW transaction_min_cagg
        WITH (timescaledb.continuous) AS
        SELECT id,
            time_bucket(INTERVAL '1 min', tm) AS bucket,
            AVG(price) as avg,
            MAX(price) as max,
            MIN(price) as min
        FROM transaction_data
        GROUP BY id, bucket;
  2. Create a nested aggregate. Create an hourly aggregate on top of the per-minute aggregate.

    CREATE MATERIALIZED VIEW transaction_one_hour_mview
        WITH (timescaledb.continuous) AS
        SELECT id,
            time_bucket(INTERVAL '1 hour', bucket) AS bucket,
            AVG(avg) as avg,
            MAX(max) as max,
            MIN(min) as min
        FROM transaction_min_cagg
        GROUP BY id, time_bucket(INTERVAL '1 hour', bucket);

Time-series compression

  • When a time-series partition is identified as historical data, you can enable the data compression feature provided by the time-series database. Ganos TSDB supports compressing entire tables or specific partitions. Compression can reduce data storage space by more than 70%, significantly lowering storage costs.

  • Compressed data is read-only.

Uninstall the plug-in

DROP EXTENSION ganos_tsdb CASCADE;
DROP EXTENSION timescaledb CASCADE;