Почему Postgres блок транзакций намного медленнее? И как решить? - PullRequest
0 голосов
/ 11 февраля 2020

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

Справочная информация:

У меня есть база данных postgres v11, работающая с некоторыми данными. Теперь есть около 200 тыс. Операций, которые я хочу выполнить как отдельную транзакцию (все они должны быть успешными или не вносить изменений). Транзакции включают в себя комбинацию операторов INSERT и UPDATE, которые также включают в себя некоторые триггеры в базе данных.

Если я выполняю запрос в транзакции, то это занимает около 70 минут

BEGIN;
// statement
// statement
// statement
// ...
COMMIT;

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

// statement
// statement
// statement
// ...

Теперь я подозреваю, что разница в производительности связана с хранением некоторых временных таблиц транзакций, которые сталкиваются с размером буфера, или проблемами индексации, что приводит к замедление, но так как у меня нет глубокого понимания внутренних органов Postgres, я фактически не знаю. Я попытался изменить различные настройки WAL, но без существенной разницы.

Итак, у меня есть 2 вопроса:

  1. Почему транзакция намного медленнее?
  2. Как можно ли выполнить пакет операций с возможностью отката без ущерба для производительности до этой степени?

РЕДАКТИРОВАТЬ 1


Дополнительная информация о таблицах и запросах

Я постараюсь дать как можно больше информации, не раскрывая подробностей (что мне не разрешено).

Существует 5 основных таблиц, связанных с этой транзакцией - accounts, currencies, transactions, transaction_logs, balance.

transactions Таблица имеет 3 внешних ключа - от 2 до accounts и от 1 до currencies.

* Таблица 1045 * имеет 2 внешних ключа - от 1 до accounts и от 1 до currencies.

transaction_logs Таблица содержит 3 внешних ключа - от 1 до transactions, от 1 до accounts, 1 на currencies.

Пакет транзакций начинается со вставки всех соответствующих счетов и валют. (около 10 000 учетных записей и 2 валют)

Далее это набор вставок и обновлений таблицы транзакций - вставки в группах по 300 строк в одной вставке, а обновления основаны на PK, поэтому только 1 грести за раз. Всего имеется около 140 тыс. Вставок и 60 тыс. Обновлений.

И вставки, и обновления в свою очередь выполняют триггеры, которые обновляют таблицу сальдо и вставляют 2 строки в таблицу транзакций.

Типичная вставка выглядит как

INSERT INTO transactions (from_account_id, to_account_id, currency_id, amount, metadata, status) VALUES (1, 1, 1, 10.00, '{"json":"blob"}', 'pending');

Типичное обновление выглядит как

UPDATE transactions SET status = 'finished' WHERE id = 1;

Среда программирования node.js и драйвер pg.


РЕДАКТИРОВАТЬ 2


Извиняюсь перед всеми за задержку с предоставлением дополнительной информации - я пытался создать закрытую тестовую среду, воспроизводящую ошибку. И при этом мне удалось приблизиться к сути проблемы. Сама структура кажется неактуальной. Замедление вызвано обновлением одной и той же строки снова и снова в рамках одной и той же транзакции.

Самым простым воспроизведением, которым я управлял, было создание таблицы с 2 полями, добавление нескольких строк, а затем многократно Обновление первого ряда. Без транзакции время работы остается относительно постоянным. Однако в рамках транзакции она начинает расти - около 100 тыс. Обновлений примерно в 3 раза выше, чем в начале. На моей машине общая разница во времени выполнения теста на 100 тыс. Обновлений была в 2,5 раза между транзакцией и версией без транзакции.

Если, однако, ваши данные более разнообразны - не обновление одной и той же строки, но разных строк, тогда проблема не происходит, и с хорошо распределенными данными транзакция на самом деле быстрее.

PS. Протестировал это также с v12 и работает примерно так же.

У кого-нибудь есть идеи, почему это происходит?


РЕДАКТИРОВАТЬ 3


Я создал хранилище для демонстрации этой проблемы https://github.com/DeadAlready/pg-test

Ответы [ 2 ]

1 голос
/ 14 февраля 2020

Я отправил вопрос в postgres официальный список рассылки, и вот ответ, который я получил, которым я поделюсь здесь для будущих искателей.


TL; DR;

  • медленное обновление одной строки несколько раз внутри транзакции
  • ожидается
  • избегайте этого

Ссылка на тему:

https://www.postgresql.org/message-id/flat/7624.1581628574%40sss.pgh.pa.us

Полный ответ:

Да неудивительно. Каждое новое обновление создает новую версию своей строки. Когда вы делаете их в отдельных транзакциях, то, как только транзакция N + 1 фиксируется, система может распознать, что версия строки, созданная транзакцией N, мертва (больше не видна никому), и утилизировать ее, позволяя количеству версий строки, присутствующих на риск остаться более или менее постоянным. Тем не менее, не существует одинаково хорошего обслуживания для версий строк, созданных транзакцией, которая все еще выполняется. Поэтому, когда вы выполняете N обновлений в одной транзакции, на диске будет N обреченных, но еще не утилизируемых версий строк.

Помимо раздувания дискового пространства, это плохо, поскольку позднее Обновления должны сканировать все версии строк, созданные предыдущими обновлениями, в поисках версии, которую они должны обновить. Таким образом, с этим связана стоимость O (N ^ 2), которая, несомненно, является тем, что вы наблюдаете.

Нет действительно хорошего решения, кроме как "не делайте этого" ». Предложенное Дэвидом предложение использовать временную таблицу не поможет, потому что это поведение одинаково, независимо от того, является ли таблица временной или обычной.

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

regards, tom lane

0 голосов
/ 11 февраля 2020
  1. Почему транзакция намного медленнее?
    • Небольшие транзакции стоят дорого из-за накладных расходов на запись в журнал. Что дублирует стоимость внешних операций.
    • Крупные транзакции стоят дорого из-за увеличения накладных расходов на управление.

По моему опыту, существует оптимальная средняя транзакция -size.

Как запустить пакет операций с возможностью отката без ущерба для производительности до такой степени?

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...