Как улучшить производительность timescaledb получения последней временной метки

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.)

Обновление: план EXPLAIN


person Antonis Christofides    schedule 13.04.2020    source источник
comment
Вы можете опубликовать свой план объяснения?   -  person Blagoj Atanasovski    schedule 14.04.2020
comment
@BlagojAtanasovski Добавил в конце вопроса.   -  person Antonis Christofides    schedule 14.04.2020


Ответы (1)


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

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

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

https://docs.timescale.com/latest/using-timescaledb/hypertables#best-practices

Уменьшение количества фрагментов значительно повысит производительность запроса.

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

person Blagoj Atanasovski    schedule 14.04.2020
comment
Извините, я забыл добавить LIMIT 1. Я ищу только последнюю временную метку, то есть, чтобы найти конец временного ряда. Я исправил это в вопросе и заменил план объяснения (хотя, по сути, он тот же). - person Antonis Christofides; 14.04.2020
comment
Я отредактирую свой ответ, но основная причина задержки остается прежней: слишком много фрагментов, и все они сканируются по индексу, чтобы найти их последнюю временную метку. Планировщик не может отсечь, какие фрагменты могут иметь последнюю запись для timeseries_id = x, поэтому он должен запросить их все. - person Blagoj Atanasovski; 14.04.2020