Як атомно замінити дані таблиці в 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.