Использование timescaledb
Обзор
Расширение timescaledb предоставляет дополнительные возможности для обработки данных временных рядов. Оно позволяет выполнять масштабируемые вставки и сложные запросы, а также повышает производительность запросов над данными временных рядов.
Пакет, требуемый для установки timescaledb
, поставляется с ADPG. Чтобы использовать timescaledb
, необходимо только выполнить команду CREATE EXTENSION в той базе данных, в которую необходимо поставить расширение:
CREATE EXTENSION timescaledb;
ПРИМЕЧАНИЕ
Если расширение timescaledb создано в базе данных template1 , используемой в качестве темплейта по умолчанию, во всех вновь создаваемых базах данных будет установлено это расширение.
|
ADPG использует версию timescaledb
2.14.2. Чтобы это проверить, можно выполнить следующий запрос:
SELECT extversion FROM pg_extension
WHERE extname = 'timescaledb';
Результат:
extversion ------------ 2.14.2
Расширение timescaledb
добавляет в базу данных ряд функций, представлений и таблиц. За дополнительной информации об API timescaledb
обратитесь к статье TimescaleDB API reference.
Вы можете использовать команду psql
\dn
для просмотра списка схем, созданных timescaledb
.
Результат:
List of schemas Name | Owner --------------------------+---------- _timescaledb_cache | postgres _timescaledb_catalog | postgres _timescaledb_config | postgres _timescaledb_debug | postgres _timescaledb_functions | postgres _timescaledb_internal | postgres public | postgres timescaledb_experimental | postgres timescaledb_information | postgres
Гипертаблицы
Расширение timescaledb
предоставляет гипертаблицы, которые являются абстракцией высокого уровня, расширяющей традиционные таблицы PostgreSQL для более эффективной работы с временными данными. Любую операцию, которая производится с обычными таблицами PostgreSQL, можно выполнить с гипертаблицами. Гипертаблица упрощает организацию и извлечение информации о временных рядах, предоставляя встроенное секционирование на основе временных интервалов. timescaledb
разбивает гипертаблицы на фрагменты (chunks).
Создание пустой гипертаблицы
Чтобы сгенерировать гипертаблицу, необходимо создать обычную таблицу и передать её в качестве параметра функции create_hypertable:
CREATE TABLE base_table(
id uuid,
time_column timestamptz not null default clock_timestamp(),
data_column1 int default random()*1E5,
data_column2 int default random()*1E5
);
SELECT create_hypertable('base_table', by_range('time_column'));
Результат:
create_hypertable ------------------- (7,t)
Число из вывода (в данном случае 7
) будет использоваться далее в именах фрагментов, принадлежащих этой гипертаблице.
Функция create_hypertable
имеет два обязательных параметра:
-
relation
— идентификатор таблицы для преобразования в гипертаблицу. Параметрrelation
должен быть типаregclass
('base_table'
в примере выше). -
dimension
— должен быть типа dimension_info. Для создания экземпляраdimension_info
необходимо вызвать функцию by_range или by_hash. Этот параметр определяет условия, в соответствии с которыми таблица будет разделена на фрагменты (by_range('time_column')
в примере выше).
Заполните гипертаблицу данными:
INSERT INTO base_table(id, time_column) SELECT gen_random_uuid(), generate_series(
now() - INTERVAL '6 months',
now(),
INTERVAL '1 minute'
);
Используйте метакоманду psql
\d+
для отображения информации о гипертаблице base_table
:
\d+ base_table
Результат:
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description --------------+--------------------------+-----------+----------+--------------------------------------------------+---------+-------------+--------------+------------- id | uuid | | | | plain | | | time_column | timestamp with time zone | | not null | clock_timestamp() | plain | | | data_column1 | integer | | | (random() * '100000'::numeric::double precision) | plain | | | data_column2 | integer | | | (random() * '100000'::numeric::double precision) | plain | | | Indexes: "base_table_time_column_idx" btree (time_column DESC) Triggers: ts_insert_blocker BEFORE INSERT ON base_table FOR EACH ROW EXECUTE FUNCTION _timescaledb_functions.insert_blocker() Child tables: _timescaledb_internal._hyper_7_57_chunk, _timescaledb_internal._hyper_7_58_chunk, _timescaledb_internal._hyper_7_59_chunk, _timescaledb_internal._hyper_7_60_chunk, _timescaledb_internal._hyper_7_61_chunk, _timescaledb_internal._hyper_7_62_chunk, _timescaledb_internal._hyper_7_63_chunk, _timescaledb_internal._hyper_7_64_chunk, _timescaledb_internal._hyper_7_65_chunk, _timescaledb_internal._hyper_7_66_chunk, _timescaledb_internal._hyper_7_67_chunk, _timescaledb_internal._hyper_7_68_chunk, _timescaledb_internal._hyper_7_69_chunk, _timescaledb_internal._hyper_7_70_chunk, _timescaledb_internal._hyper_7_71_chunk, _timescaledb_internal._hyper_7_72_chunk, _timescaledb_internal._hyper_7_73_chunk, _timescaledb_internal._hyper_7_74_chunk, _timescaledb_internal._hyper_7_75_chunk, _timescaledb_internal._hyper_7_76_chunk, _timescaledb_internal._hyper_7_77_chunk, _timescaledb_internal._hyper_7_78_chunk, _timescaledb_internal._hyper_7_79_chunk, _timescaledb_internal._hyper_7_80_chunk, _timescaledb_internal._hyper_7_81_chunk, _timescaledb_internal._hyper_7_82_chunk, _timescaledb_internal._hyper_7_83_chunk, _timescaledb_internal._hyper_7_84_chunk Access method: heap
В выводе вы можете увидеть, что для base_table
создано 28 фрагментов.
Также можно указать интервал для фрагментов. Для этого передайте интервал в качестве параметра в функцию by_range
. Его можно задать в миллисекундах:
CREATE TABLE table1 (LIKE base_table);
SELECT create_hypertable('table1', by_range('time_column', 86400000000));
А также можно использовать ключевое слово INTERVAL
:
CREATE TABLE table2 (LIKE base_table);
SELECT create_hypertable('table2', by_range('time_column', INTERVAL '1 day'));
При создании гипертаблицы timescaledb
генерирует индексы по столбцам секционирования. Параметр create_default_indexes
, управляющий этим поведением, является необязательным параметром, его значение по умолчанию — true
. Вы можете отменить создание индексов, установив его значение равным false
:
CREATE TABLE table3 (LIKE base_table);
SELECT create_hypertable('table3', by_range('time_column'), create_default_indexes=> false);
Если вы попытаетесь вызвать функцию create_hypertable
для таблицы, которая уже является гипертаблицей, возникнет ошибка: ERROR: table "base_table" is already a hypertable
. Вы можете установить необязательный параметр if_not_exists
в true
, чтобы вывести предупреждение вместо генерации ошибки:
SELECT create_hypertable('base_table', by_range('time_column'), if_not_exists=> true);
Результат:
NOTICE: table "base_table" is already a hypertable, skipping create_hypertable ------------------- (7,f) (1 row)
Преобразование таблицы с данными в гипертаблицу
Чтобы создать гипертаблицу из таблицы с данными, укажите необязательный параметр — migrate_data
. Код ниже создает новую таблицу и заполняет её данными:
CREATE FUNCTION return_location() RETURNS text AS $$
BEGIN
RETURN 'location ' || floor(random() * 5+1 ):: text;
END;
$$ LANGUAGE plpgsql;
CREATE TABLE "conditions"(
time TIMESTAMP WITH TIME ZONE NOT NULL,
location TEXT DEFAULT return_location(),
temperature NUMERIC,
humidity NUMERIC
);
INSERT INTO conditions(time, temperature, humidity) SELECT generate_series(
now() - INTERVAL '6 months',
now(),
INTERVAL '1 minute'
)as time,
round(random()*100) as temperature,
round(random()*100) as humidity;
Преобразуйте conditions
в гипертаблицу, передав параметр migrate_data
:
SELECT create_hypertable('conditions', 'time', migrate_data => true);
Результат:
NOTICE: migrating data to chunks DETAIL: Migration might take a while depending on the amount of data. create_hypertable -------------------------- (14,public,conditions,t)
Если таблица для преобразования содержит данные, но параметр migrate_data
не установлен или равен false
, возникнет ошибка:
ERROR: table "conditions" is not empty HINT: You can migrate data by specifying 'migrate_data => true' when calling this function.
Компрессия
Расширение timescaledb
позволяет сжимать данные временных рядов и значительно уменьшать размер фрагмента. При включении компрессии данные в гипертаблице сжимаются фрагмент за фрагментом. При сжатии фрагмента несколько записей объединяются в одну строку. Это уменьшает объем необходимого дискового пространства, поскольку одна строка занимает меньше места, чем несколько строк. Для получения дополнительной информации об этом функционале см. About compression.
Чтобы включить компрессию, выполните следующие действия.
-
Используйте команду ALTER TABLE, чтобы включить компрессию и указать её параметры:
ALTER TABLE conditions SET ( timescaledb.compress, timescaledb.compress_segmentby = 'location' );
-
Вызовите функцию add_compression_policy, чтобы добавить политику компрессии. Следующий код сжимает фрагменты, которые старше 1 часа:
SELECT add_compression_policy('conditions', INTERVAL '1 hour');
Также существует возможность распаковать определенный фрагмент с помощью функции decompress_chunk, например, чтобы максимизировать производительность, если предстоит выполнить большое количество различных операций DML. Можно использовать представление timescaledb_information.chunks, чтобы определить имя фрагмента и передать его в качестве параметра decompress_chunk
.
SELECT decompress_chunk('_timescaledb_internal._hyper_14_376_chunk');
Используйте функцию remove_compression_policy для удаления компрессии:
SELECT remove_compression_policy('conditions');
Использование гиперфункций timescaledb для анализа данных
Расширение timescaledb
предоставляет гиперфункции для анализа данных. В этом разделе рассматриваются некоторые из них. Примеры в этом разделе используют таблицу conditions
, создание которой описано выше.
first и last
Функции first и last позволяют получить первое или последнее значение одного столбца среди значений, упорядоченных по другому столбцу. Следующий пример возвращает самое раннее значение температуры на основе времени из столбца time
в каждой группе значений, указанных в столбце location
:
SELECT location, first(temperature, time) FROM conditions
GROUP BY location ORDER BY location;
Результат:
location | first ------------+------- location 1 | 29 location 2 | 30 location 3 | 43 location 4 | 87 location 5 | 95
Приведенный ниже код возвращает последнее значение влажности на основе времени из столбца time
в каждой группе значений, указанных в столбце location
:
SELECT location, last(humidity, time) FROM conditions
GROUP BY location ORDER BY location;
Результат:
location | last ------------+------ location 1 | 15 location 2 | 69 location 3 | 47 location 4 | 98 location 5 | 69
time_bucket
Функция time_bucket позволяет объединять данные в интервалы времени (time buckets).
Следующий запрос вычисляет среднюю температуру за день для определенного местоположения:
SELECT time_bucket('1 day', time) AS bucket_time, AVG(temperature) AS avg_temperature
FROM conditions WHERE location = 'location 3'
GROUP BY bucket_time ORDER BY bucket_time
LIMIT 5;
Результат:
bucket_time | avg_temperature ------------------------+--------------------- 2024-09-02 00:00:00+00 | 49.0534759358288770 2024-09-03 00:00:00+00 | 49.6323529411764706 2024-09-04 00:00:00+00 | 49.0774410774410774 2024-09-05 00:00:00+00 | 47.7973856209150327 2024-09-06 00:00:00+00 | 51.7582417582417582
histogram
Функция histogram вычисляет, сколько раз значение попадает в указанный диапазон. Например, запрос ниже разбивает диапазон влажности 0-100
на 10 диапазонов и возвращает, сколько раз значение влажности попадает в каждый диапазон для каждого местоположения.
SELECT location, histogram(humidity, 0, 100, 10)
FROM conditions GROUP BY location ORDER BY location;
Возвращаемое значение — массив, содержащий указанное количество диапазонов плюс два. Первый диапазон в начале массива для значений ниже минимальной границы, а последний диапазон для значений, больше или равных максимальной границе:
location | histogram ------------+----------------------------------------------------------- location 1 | {0,4903,5316,5124,5226,5200,5219,5386,5173,5206,5172,278} location 2 | {0,4830,5338,5343,5150,5222,5189,5154,5333,5230,5275,267} location 3 | {0,4949,5264,5217,5216,5194,5278,5077,5214,5190,5246,268} location 4 | {0,4922,5075,5192,5134,5329,5083,5349,5302,5128,5262,277} location 5 | {0,4892,5224,5104,5290,5050,5291,5179,5140,5191,5320,260}
Значения, равные минимальному значению диапазона, включаются в него, а значения, равные максимальному значению, относятся к следующему диапазону. Тестовые данные примера содержат 1350 значений влажности, которые равны 100
. В результате последний (двенадцатый) диапазон содержит это значение, распределенное по местоположениям (location 1 — 278, location 2 — 267, location 3 — 268, location 4 — 277, location 1 — 260).