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

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

Таблица records содержит в основном все данные из каждой необработанной записи, кроме имени, которое заменяется идентификатором.указывая на глобальную таблицу names, где столбец name уникален.

Я использую следующий запрос для ETL.Для каждой партии из 5 тыс. Записей я создаю одну транзакцию с 5 тыс. Операторов после этого запроса:

WITH new_id AS (
    INSERT INTO names
    VALUES (
        DEFAULT,
        @raw_name
    )
    ON CONFLICT (name) 
        DO UPDATE
            SET id = (
                SELECT id FROM names WHERE name = @raw_name
            )
        RETURNING id
)
INSERT INTO records VALUES (
    DEFAULT,
    (SELECT id FROM new_id),
    -- other (constant) stuff
);

Цель здесь - вставить имя в таблицу names, если оно еще не существует.В обоих случаях идентификатор имени извлекается и прикрепляется к записи, которая вставляется в таблицу records.

Выполнение транзакции занимает около 2,5 с для 5000 записей, и я ищу для оптимизации времени выполненияэтот запрос.Временное выделение памяти или таблицы допустимо.Я также могу влиять на размер партии (мин. 1 КБ).Я должен работать с существующей схемой (две таблицы).

(Мне также интересно, есть ли способ оптимизировать это с помощью параллелизма. Я могу запускать сразу несколько заданий ETL, но запрос в том виде, в каком он есть, немедленно заходит в тупик.)

1 Ответ

0 голосов
/ 23 сентября 2018

Ваша проблема производительности может быть связана с тем, что вы выполняете более 5000 команд вставки.Это очень программный способ выполнения работы, который не может быть оптимизирован движком PostgreSQL.

Вам следует попробовать более декларативный подход, при котором вы манипулируете множествами строк, а не построчно.Этот способ должен позволить PostgreSQL выполнять более эффективную работу:

BEGIN;

CREATE TEMPORARY TABLE batch (
  name character varying not null,
  txt character varying not null
);

-- INSERT or COPY, depending on where the data comes from
INSERT INTO batch (txt, name) 
VALUES 
  ('txt1','a'),
  ('txt2','a'),
  ('txt3','a'),
  ('txt4','b'),
  ('txt5','b');

INSERT INTO names (name)
  SELECT name FROM batch
  ON CONFLICT DO NOTHING;

INSERT INTO records (name_id, txt)
  SELECT names.id as name_id, batch.txt 
  FROM batch JOIN names ON names.name = batch.name;
COMMIT;
...