Как уменьшить количество проверок перед вставкой в ​​тот или иной раздел? - PullRequest
0 голосов
/ 17 мая 2019

Таблицы в базе данных разбиты по неделям на основе метки времени данных, т.е. tablename_y2019w20.Но когда были введены разделы, postgres начал занимать слишком много времени процессора.

Статистика использования ЦП сбора при запуске


SELECT substring(query, 1, 50) AS short_query, round(total_time::numeric, 2) AS total_time, calls, rows, round(total_time::numeric / calls, 2) AS avg_time, round((100 * total_time / sum(total_time::numeric) OVER ())::numeric, 2) AS percentage_cpu FROM pg_stat_statements ORDER BY total_time DESC LIMIT 20;

показала, что узким местом является утверждение SELECT NOT EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name=tablename) из триггерафункция (см. ниже), которая выполняется при каждой вставке.

Например, EXPLAIN ANALYZE массовой вставки 18 элементов, которая происходит каждую секунду, выглядит следующим образом:

Planning time: 0.787 ms
 Trigger before_insert_data_trigger: time=253.374 calls=18
 Execution time: 254.161 ms

Но так как нам нужно создавать таблицы разделов только раз в неделю, все операторы в большинстве случаев бесполезны.Но я не могу создать разделы за пределами postgres заранее.

Можно ли запланировать создание разделов в postgres, например, только каждое воскресенье?

Вот функция и соответствующий триггер:

CREATE OR REPLACE FUNCTION data_insert_child_date()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
                DECLARE
                    match data."timestamp"%TYPE;
                    checks TEXT;
                    tablename_parent text := "data";
                    tablename text;
                BEGIN
                    IF NEW."timestamp" IS NULL THEN
                        tablename := tablename_parent||'_null';
                        checks := '"timestamp" IS NULL';
                    ELSE
                         match := DATE_TRUNC('week', NEW."timestamp");
                        tablename := tablename_parent||'_' || TO_CHAR(NEW."timestamp", '"y"IYYY"w"IW');
                        checks := '"timestamp" >= ''' || match || ''' AND "timestamp" < ''' || (match + INTERVAL '1 week') || '''';
                    END IF;

                    IF NOT EXISTS(
                        SELECT 1 FROM information_schema.tables WHERE table_name=tablename)
                    THEN
                        BEGIN
                            EXECUTE 'CREATE TABLE part.' || tablename || ' (
                                CHECK (' || checks || '),
                                LIKE "data" INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES
                            ) INHERITS (part."'||tablename_parent||'");

                        ';
                        EXCEPTION WHEN duplicate_table THEN
                            -- pass
                        END;
                    END IF;

                    EXECUTE 'INSERT INTO part.' || tablename || ' VALUES (($1).*);' USING NEW;
                    RETURN NEW;
                END;
            $function$;

Итриггер

CREATE TRIGGER before_insert_data_trigger BEFORE INSERT
    ON data
    FOR EACH ROW
    EXECUTE PROCEDURE data_insert_child_date();

1 Ответ

0 голосов
/ 17 мая 2019

Расписание?

Вы можете планировать только с помощью внешнего инструмента, такого как cron или pgagent, и вам придется сделать это, прежде чем вам понадобится этот раздел или убедиться, что ничто не может вставить данные перед заданием cronjob или pgagent (или любым другим) сделано с созданием раздела.Предполагая, что, как вы упомянули, вы не можете делать разделы заранее.Но тогда вам все равно придется создать их для всех таблиц или каким-то образом выяснить, какие таблицы им нужны (например, вставить данные того дня в родительскую таблицу и затем переместить их в раздел в конце дня или недели).

Проверка только в воскресенье

Вы можете ограничить проверку раздела только воскресеньями:

IF extract(dow FROM current_date) = 7 /*maybe = 0 in US*/ AND NOT EXISTS(..

Но если этот триггер не будет выполнен в воскресенье по какой-либо причине (в этот день нет данных, сервер отключен), тогда у вас не будет раздела для этой конкретной недели, как только наступит понедельник.

Оптимизация проверки раздела

Вы можете сделать эту проверку намного быстрее, если не будете использовать information_schema.tables.В каталоге, содержащем ~ 100000 строк (индексы, таблицы, представления и т. Д.), Проверка того, существует ли одна таблица, занимает около 100 мс на моем компьютере, тогда как выполнение такой же проверки на pg_catalog.pg_class напрямую занимает менее 1 мс.

--pg 9.5 and up
SELECT 1 FROM pg_class
WHERE relname = 'table_name'
  AND relnamespace = 'schema_name'::regnamespace;

--pg 9.4
SELECT 1 FROM pg_class
WHERE relname = 'table_name'
  AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'schema_name');

Что я делаю

Лично я думаю, что я бы просто создавал несколько разделов заранее каждые несколько месяцев.Это то, что мы имеем у моего нынешнего работодателя.Конечно, лучше перейти на PG 11, но я понимаю, что это может быть невозможным.

...