Як атомно замінити дані таблиці в PostgreSQL


14

Я хочу замінити весь вміст таблиці, не впливаючи на будь-які вхідні SELECTзаяви під час процесу.

Випадок використання - мати таблицю, в якій зберігається інформація про поштові скриньки, яка регулярно витягується, і її потрібно зберігати в таблиці PostgreSQL. Є багато клієнтів, які використовують додаток, який постійно запитує ту саму таблицю.

Зазвичай я б робив щось на кшталт (вхідний псевдокод) ...

BEGIN TRANSACTION
TRUNCATE TABLE
INSERT INTO
COMMIT

Але, на жаль, таблицю неможливо прочитати під час цього процесу; за рахунок часу, необхідного INSERT INTOдля його завершення. Стіл заблокований.

У MySQL я використовував би їх атомну RENAME TABLEкоманду, щоб уникнути цих проблем ...

CREATE TABLE table_new LIKE table; 
INSERT INTO table_new;
RENAME TABLE table TO table_old, table_new TO table; *atomic operation*
DROP TABLE table_old;

Як я міг досягти цього в PostgreSQL?

Для цілей цього питання ви можете припустити, що я не використовую сторонні ключі.


Чому ви вважаєте, що таблицю неможливо прочитати, вставляючи в неї рядки? Обрізана таблиця матиме негайне дію на всіх сесіях; однак вкладиші (якщо вони будуть зроблені всередині транзакції, яка завершує їх усі, як підказує ваш псевдо-код), не буде видимою для інших сеансів, поки ви не зробите їх. Інші сеанси можна буде вибирати з таблиці та побачити порожню таблицю, поки ви не виконаєте.
zgguy

2
@zgguy в TRUNCATEкоманду придбає AccessExclusive замок на столі, так що ніхто не зможе не читати з таблиці до цієї транзакції або відкат.
Джош Купершмідт

2
Якщо ви використовуєте deleteзамість truncateнього, буде повільніше, але без блокування читачів. Скільки рядків потрібно видалити?
a_horse_with_no_name

@a_horse_with_no_name Зазвичай між 200-300k рядками з багатьма колонками varchar. Час очікування DELETEі INSERTбув би занадто довгим.
Кларк

Відповіді:


21

Правильно, TRUNCATE TABLE команда, яку ви виконуєте, "... набуває блокування ACCESS EXCLUSIVE на кожній таблиці, над якою вона працює ", тому в першому блоці SQL, який ви опублікували, будь-які інші клієнти, які намагаються отримати доступ до таблиці після цього часу, будуть заблоковані до INSERTзавершення. і ти COMMIT.

Ви можете використовувати той самий спосіб вирішення, що і у вашому коді MySQL; Postgres підтримує приблизно той самий синтаксис і матиме схожу поведінку блокування. А саме:

BEGIN;
-- You probably want to make sure that no one else is
-- INSERT / UPDATE / DELETE'ing from the original table, otherwise
-- those changes may be lost during this switchover process. One way
-- to do that would be via:
-- LOCK TABLE "table" IN SHARE ROW EXCLUSIVE mode;
CREATE TABLE "table_new" (LIKE "table");
INSERT INTO "table_new" ...;

-- The ALTER TABLE ... RENAME TO command takes an Access Exclusive lock on "table",
-- but these final few statements should be fast.
ALTER TABLE "table" RENAME TO "table_old";
ALTER TABLE "table_new" RENAME TO "table";
DROP TABLE "table_old";

COMMIT;

Додатковий бонус: Postgres насправді підтримує транзакційний DDL, на відміну від MySQL, тому у випадку, якщо вам потрібно буде РОБОТИВАТИ вищевказану транзакцію, ви можете сміливо робити це.


Я збираюся зробити тестування на це, дякую за вашу відповідь. Якщо я використовував LOCK TABLEзапропонований вами метод, мені потрібно буде його розблокувати ще раз COMMIT, або він розблокує себе?
Кларки

1
EDIT: У цій документації знайдено такий текст : "Немає команди UNLOCK TABLE; блоки завжди вивільняються в кінці транзакції."
Кларк

2
Тут бракує однієї суми обмежень, які досі належать_old
Intellix

@Intellix ви можете детальніше розглянути це? Чи означає це, що обмеження просто називаються за старою таблицею або вони стосуються лише старої таблиці (це означає, що обмеження фактично скасовуються)?
maerics

Коментар перед створенням таблиці ( -- LOCK TABLE "table" IN ROW EXCLUSIVE mode;) видається недостатнім для захисту від оновлення / вставки в таблицю джерел відповідно до специфікацій. Два ROW EXCLUSIVEблокування можна придбати без будь-якого конфлікту (див. Таблицю 13.2 у postgresql.org/docs/10/explicit-locking.html#LOCKING-TABLES ). Для запобігання оновлення даних вам потрібно принаймні SHAREблокувати.
Пілу
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.