Postgres: оптимизация одновременных обновлений одной строки

#postgresql #go #sql-update

#postgresql #Вперед #sql-обновление

Вопрос:

ПРОБЛЕМА

Я работаю с PostgreSQL v10 golang и сталкиваюсь с тем, что я считаю очень распространенной проблемой SQL:

  • У меня есть таблица «счетчики», в которой есть current_value и max_value целочисленные столбцы.
  • Строго говоря, один раз current_value >= max_value я хотел бы отклонить запрос.
  • У меня есть несколько модулей Kubernetes, которые при каждом вызове API могут увеличивать current_value количество одной и той же строки (в худшем случае) в таблице «счетчики» на 1 (можно рассматривать как одновременные обновления одной и той же базы данных с распределенных хостов).

В моей текущей и наивной реализации несколько ОБНОВЛЕНИЙ для одной и той же строки естественным образом блокируют друг друга (уровень изоляции «зафиксирован для чтения», если это имеет значение). В худшем случае у меня более 10 запросов в секунду, которые обновляют одну и ту же строку. Это создает бутылочное горлышко и снижает производительность, чего я не могу себе позволить.


ВОЗМОЖНОЕ РЕШЕНИЕ

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

Пока счетчик current_value находится на относительно безопасном расстоянии от max_value (дельта > 100), отправляйте запрос на обновление на канал, который будет сбрасываться каждую секунду или около того работником, который будет агрегировать обновления и запрашивать их сразу. В противном случае (дельта <= 100) выполните обновление в контексте транзакции (и устраните узкое место, но в меньшинстве случаев). Это ускорит запросы на обновление до тех пор, пока предел почти не будет достигнут, эффективно устраняя узкое место.


Вероятно, это сработало бы для решения моей проблемы. Однако я не могу не думать, что есть лучшие способы решения этой проблемы.

Я не нашел отличного решения в Интернете, и хотя мой эвристический метод сработал бы, он кажется нечистым и ему не хватает целостности.

Креативные решения очень приветствуются!


Редактировать:

Благодаря совету @laurenz-albe я попытался сократить продолжительность между ОБНОВЛЕНИЕМ, при котором строка блокируется, и фиксацией транзакции. Похоже, что нажатие всех ОБНОВЛЕНИЙ до конца транзакции сделало свое дело. Теперь я могу обрабатывать более 100 запросов в секунду и поддерживать целостность!

Ответ №1:

10 одновременных обновлений в секунду — это смехотворно мало. Просто убедитесь, что транзакции как можно короче, и это не будет проблемой.

Вашей самой большой проблемой будет VACUUM , поскольку большое количество обновлений — это наихудшая возможная рабочая нагрузка для PostgreSQL. Убедитесь, что вы создали таблицу с fillfactor числом 70 или около того, и она current_value не проиндексирована, чтобы получать ГОРЯЧИЕ обновления.

Комментарии:

1. Вы правы. Проблема заключается в большой продолжительности моих транзакций, поскольку каждая транзакция длится в течение всего срока действия запроса. Я попытаюсь создать дополнительную транзакцию, только для этого обновления, и попытаюсь придумать способ отменить обновление в случае сбоя основной транзакции (запроса).

2. Доведение ОБНОВЛЕНИЯ до самого конца транзакции сделало свое дело. Теперь я могу обрабатывать более 100 запросов в секунду. Спасибо.