Как улучшить производительность timescaledb, получая последнюю отметку времени - PullRequest
1 голос
/ 13 апреля 2020

SELECT timeseries_id, "timestamp" FROM enhydris_timeseriesrecord WHERE timeseries_id=6661 ORDER BY "timestamp" DESC LIMIT 1;

(таблица содержит около 66 м записей, а записи с timeseries_id = 6661 - около 0,5 м).

Выполнение этого запроса занимает около 1-2 секунд, что я нахожу слишком много.

Если он использовал простой индекс btree, он должен найти то, что ищет после примерно 30 итераций. Насколько я вижу, когда я выполняю EXPLAIN ANALYZE для этого запроса, он использует индекс, но он должен делать это в каждом чанке, и, очевидно, имеется 1374 чанка.

Как запрос может стать быстрее?

                 Table "public.enhydris_timeseriesrecord"
    Column     |           Type           | Collation | Nullable | Default 
---------------+--------------------------+-----------+----------+---------
 timeseries_id | integer                  |           | not null | 
 timestamp     | timestamp with time zone |           | not null | 
 value         | double precision         |           |          | 
 flags         | character varying(237)   |           | not null | 
Indexes:
    "enhydris_timeseriesrecord_pk" PRIMARY KEY, btree (timeseries_id, "timestamp")
    "enhydris_timeseriesrecord_timeseries_id_idx" btree (timeseries_id)
    "enhydris_timeseriesrecord_timestamp_idx" btree ("timestamp" DESC)
    "enhydris_timeseriesrecord_timestamp_timeseries_id_idx" btree ("timestamp", timeseries_id)
Foreign-key constraints:
    "enhydris_timeseriesrecord_timeseries_fk" FOREIGN KEY (timeseries_id) REFERENCES enhydris_timeseries(id) DEFERRABLE INITIALLY DEFERRED
Triggers:
    ts_insert_blocker BEFORE INSERT ON enhydris_timeseriesrecord FOR EACH ROW EXECUTE PROCEDURE _timescaledb_internal.insert_blocker()
Number of child tables: 1374 (Use \d+ to list them.)

Обновление : ОБЪЯСНИТЬ план

1 Ответ

2 голосов
/ 14 апреля 2020

База данных должна go к подиндексам каждого чанка и найти находку, которая является самой последней отметкой времени для timeseries_id = x. База данных правильно использует индекс (как вы можете видеть из объяснения), она выполняет сканирование индекса, а не полное сканирование каждого субиндекса в каждом из фрагментов. Так что он делает> 1000 сканирований индекса. Нельзя обрезать чанки, потому что планировщик не может знать, какие чанки имеют записи для этого конкретного c timeseries_id.

И у вас есть 1300 блоков только для 66-метровых записей -> ~ 50 тыс. Строк на блок. Это слишком мало строк на кусок. Из документов шкалы времени они имеют следующие рекомендации:

Ключевым свойством выбора временного интервала является то, что блок (включая индексы), принадлежащий самому последнему интервалу (или фрагментам, если используются разделы пространства), соответствуют в память. Поэтому мы обычно рекомендуем установить интервал так, чтобы эти блоки составляли не более 25% основной памяти.

https://docs.timescale.com/latest/using-timescaledb/hypertables#best -практики

Сокращение количества чанков значительно повысит производительность запросов.

Кроме того, вы можете получить еще большую производительность запросов, если будете использовать сжатие TimescaleDB, которое сократит количество фрагментов, необходимых для сканирования, еще больше, вы можете сегментировать по timeseries_id (https://docs.timescale.com/latest/api#compression ) Или вы можете определить непрерывный агрегат, который будет содержать последний элемент в timeseries_id (https://docs.timescale.com/latest/api#continuous -агрегатов )

...