Skip to content

Performance best practice suggestions #32

Description

@dino2gnt

Hi,

Let me preface this by stating that I am not a PostgreSQL expert, so if there's something obvious I'm not understanding I apologize.

I'm writing a plugin that leverages pg_timeseries in an open source enterprise network monitoring platform. I'm using a simple time series schema:

                                   Partitioned table "public.pgtimeseries_time_series"
 Column |           Type           | Collation | Nullable | Default | Storage  | Compression | Stats target | Description
--------+--------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 key    | text                     |           | not null |         | extended |             |              |
 time   | timestamp with time zone |           | not null |         | plain    |             |              |
 value  | double precision         |           |          |         | plain    |             |              |
Partition key: RANGE ("time")
Indexes:
    "pgtimeseries_time_series_key_idx" btree (key)
    "pgtimeseries_time_series_time_idx" btree ("time" DESC)

There are currently several hundred unique keys, but this could grow to tens of thousands for busy instances, making a table per datasource untenable.

# select count(distinct key) from pgtimeseries_time_series;
 count
-------
   637
(1 row)

Samples are collected every five minutes, but the interval is configurable per data source so it could be more or less frequently.

When using date_bin_table for time bucketting, I'm getting query plan results showing a lot of temp IO, as ~a million rows are selected and the filtered during the function scan, resulting in several hundred MB of read and write IO per query. I assume this is caused by the date_bin_table function selecting everything between the start time and end time, then filtering out rows that don't match the key?

=# explain (analyze, buffers) SELECT time AS step, COALESCE(avg(value), 'NaN') as aggregation FROM date_bin_table(NULL::pgtimeseries_time_series, '361 Seconds', '[2024-09-13 08:37:24.305, 2024-09-20 08:37:24.305]') where (key='name=G1YngCollCnt_resourceId=snmp/1/jvm/java_lang_type_GarbageCollector_name_G1_Young_Generation' OR key is null) GROUP BY step ORDER BY step;
                                                                            QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
 GroupAggregate  (cost=12.92..13.12 rows=10 width=16) (actual time=1400.602..1401.472 rows=1676 loops=1)
   Group Key: "time"
   Buffers: shared hit=116131 read=5947 written=5, temp read=53560 written=53607
   ->  Sort  (cost=12.92..12.94 rows=10 width=16) (actual time=1400.590..1400.746 rows=2016 loops=1)
         Sort Key: "time"
         Sort Method: quicksort  Memory: 159kB
         Buffers: shared hit=116131 read=5947 written=5, temp read=53560 written=53607
         ->  Function Scan on date_bin_table  (cost=0.25..12.75 rows=10 width=16) (actual time=1243.910..1399.759 rows=2016 loops=1)
               Filter: ((key = 'name=G1YngCollCnt_resourceId=snmp/1/jvm/java_lang_type_GarbageCollector_name_G1_Young_Generation'::text) OR (key IS NULL))
               Rows Removed by Filter: 994966
               Buffers: shared hit=116128 read=5947 written=5, temp read=53560 written=53607
 Planning:
   Buffers: shared hit=19 read=2
 Planning Time: 0.279 ms
 Execution Time: 1416.850 ms
(15 rows)

See also: https://explain.depesz.com/s/G4gq

Granted, this development instance is quite under-resourced and the clock time here is illustrative of that, but as the total number of keys grows the absolute number of rows between a given start and end will grow, creating even more IO as more rows are selected and discarded.

Is there something in my schema design or query that violates the expected best practice for pg_timeseries that I should change to mitigate this circumstance? Are there indexes that date_bin_table expects or could take advantage of that I am missing?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions