Отладка проблем с производительностью с CTE - PullRequest
0 голосов
/ 30 сентября 2018

У нас есть сценарий использования в нашем приложении, где мы сначала копируем CSV-файл в таблицу staging , а затем вставляем проверенные данные во вторую таблицу участников .Вновь созданный идентификатор участника (первичный ключ) затем обновляется в таблице staging для дальнейшей обработки.

В нашем приложении мы постоянно сталкиваемся с проблемами производительности.Иногда эта процедура работает с 100 000 строк за 15-20 секунд.Иногда это никогда не закончится в разумное время (pg_cancel_backend к спасению).

Когда я попытался создать настолько достойный минимальный тестовый пример, я не смог воспроизвести проблему: /.Так что это попытка получить некоторый совет, как отлаживать дальше или переписать базовый запрос.

  • PHP-приложение с Doctrine DBAL
  • Postgres 10,5

Мы делаем это с помощью CTE - в основном так:

WITH inserted_participants AS (
    INSERT INTO participants (email, project_id, survey_token, participant_uname)
    SELECT
        staging.email,
        1,
        staging.generated_token,
        staging.email -- is used as uname
    FROM
        staging
    RETURNING
        participants.participant_id,
        participants.participant_uname
) -- Update existing staging data with newly created participant_id
UPDATE
    staging  AS stage_update
SET
    resulting_participant_id = inserted_participants.participant_id
FROM
    inserted_participants
WHERE stage_update.email = inserted_participants.participant_uname;

Еще раз: я не могу воспроизвести проблемы с производительностью в этом тестовом примере.Я подозреваю, что это как-то связано с CTE.

Можно ли это переписать без использования CTE и при этом безопасно возвращать вновь созданные строки и обновлять их в промежуточной таблице?

Это структура таблицы для минимального тестового случая:

CREATE EXTENSION IF NOT EXISTS citext;

CREATE EXTENSION IF NOT EXISTS "pgcrypto";

DROP TABLE IF EXISTS public.staging;

CREATE TABLE public.staging
(
    staging_id serial,
    email citext COLLATE pg_catalog."default",
    generated_token character varying(255) COLLATE pg_catalog."default",
    resulting_participant_id integer,
    CONSTRAINT staging_pkey PRIMARY KEY (staging_id),
    CONSTRAINT unique_generated_token UNIQUE (generated_token)
);

CREATE INDEX ON public.staging (email);
CREATE INDEX ON public.staging (generated_token);

DROP TABLE IF EXISTS public.participants;

CREATE TABLE public.participants
(
    participant_id serial,
    email citext COLLATE pg_catalog."default" NOT NULL,
    project_id integer NOT NULL,
    survey_token character varying(255) COLLATE pg_catalog."default" NOT NULL,
    participant_uname citext COLLATE pg_catalog."default" NOT NULL,
    CONSTRAINT participants_pkey PRIMARY KEY (participant_id),
    CONSTRAINT participants_participant_uname_project_id_key UNIQUE (participant_uname, project_id),
    CONSTRAINT participants_project_id_email_key UNIQUE (project_id, email),
    CONSTRAINT participants_project_id_participant_uname_key UNIQUE (project_id, participant_uname),
    CONSTRAINT participants_survey_token_key UNIQUE (survey_token)
);

CREATE INDEX ON public.participants (participant_uname);
CREATE INDEX ON public.participants (project_id);

И фиктивные данные, которые я использовал:

INSERT INTO 
    staging (staging_id, email, generated_token)
SELECT 
    generate_series(1,100000),
    gen_random_uuid()::citext,
    gen_random_uuid()::TEXT;

1 Ответ

0 голосов
/ 01 октября 2018

Сначала вы должны определить, застряли ли вы в замке.Содержит ли pg_locks строки с идентификатором процесса долгосрочного бэкэнда и granted = FALSE?

Если это не так, найдите узкое место.Бэкэнд-процесс насыщает процессор?Ваша подсистема ввода-вывода постоянно занята?

Вы также должны использовать EXPLAIN для проверки плана выполнения.Есть ли что-то подозрительное, что могло бы объяснить продолжительность?

Может быть очень полезно проверить запрос на меньшем наборе данных, где он заканчивается.Это позволит вам запустить EXPLAIN (ANALYZE, BUFFERS), что является лучшей отправной точкой для отладки вашего запроса.Сначала проверьте, если у вас тот же план выполнения.

Одна вещь, которая может значительно замедлить изменение данных, это индексы.У тебя их много?Часто быстрее всего удалить все индексы и ограничения перед массовым обновлением, а затем заново создать их.

...