Cómo reemplazar atómicamente los datos de la tabla en PostgreSQL


14

Quiero reemplazar todo el contenido de una tabla, sin afectar las SELECTdeclaraciones entrantes durante el proceso.

El caso de uso es tener una tabla que almacene la información del buzón que se extrae regularmente y que debe almacenarse en una tabla PostgreSQL. Hay muchos clientes que usan una aplicación que consulta constantemente esa misma tabla.

Normalmente, haría algo como (pseudocódigo entrante) ...

BEGIN TRANSACTION
TRUNCATE TABLE
INSERT INTO
COMMIT

Pero desafortunadamente la tabla no se puede leer durante este proceso; debido al tiempo que lleva INSERT INTOcompletarlo. La mesa está cerrada.

En MySQL, habría usado su RENAME TABLEcomando atómico para evitar estos problemas ...

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;

¿Cómo podría lograr esto en PostgreSQL?

A los fines de esta pregunta, puede suponer que no estoy usando claves foráneas.


¿Por qué crees que la tabla no se puede leer al insertar filas en ella? Truncar tabla tendrá un efecto inmediato en todas las sesiones; sin embargo, las inserciones (si se realizan dentro de una transacción que las envuelve a todas, como sugiere su pseudocódigo) no serán visibles para otras sesiones hasta que se confirme. Otras sesiones podrán seleccionar de la tabla y verán una tabla vacía hasta que se comprometa.
zgguy

2
@zgguy el TRUNCATEcomando adquirirá un bloqueo AccessExclusive en la tabla, por lo que nadie más podrá leer de la tabla hasta que esa transacción se confirme o se revierta.
Josh Kupershmidt

2
Si lo usa deleteen su lugar truncateserá más lento, pero sin bloquear lectores. ¿Cuántas filas necesitas eliminar?
a_horse_with_no_name

@a_horse_with_no_name Por lo general, entre 200-300k filas con muchas columnas varchar. El tiempo de espera de DELETEy INSERTsería demasiado largo.
Clarkey

Respuestas:


20

Correcto, el TRUNCATE TABLE comando que está ejecutando "... adquiere un bloqueo ACCESO EXCLUSIVO en cada tabla en la que opera ", por lo que en el primer bloque SQL que publicó, cualquier otro cliente que intente acceder a la tabla después de ese tiempo se bloqueará hasta que INSERTfinalice y usted COMMIT.

Puede usar la misma solución alternativa que en su código específico de MySQL; Postgres admite aproximadamente la misma sintaxis y tendrá un comportamiento de bloqueo similar. Esto es:

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;

Bonificación adicional: Postgres en realidad admite DDL transaccional, a diferencia de MySQL, por lo que en caso de que necesite ROLLBACK la transacción anterior, puede hacerlo de forma segura.


Voy a hacer algunas pruebas sobre esto, gracias por su respuesta. Si utilicé el LOCK TABLEmétodo que sugirió, ¿tendría que desbloquearlo nuevamente antes COMMITo se desbloqueará solo?
Clarkey

1
EDITAR: Encontró la siguiente declaración en esta documentación : "No hay un comando UNLOCK TABLE; los bloqueos siempre se liberan al final de la transacción".
Clarkey

2
Una cosa que falta aquí es todas las restricciones adjuntas que aún pertenecen a_old
Intellix

@ Intellix, ¿puedes explicar eso? ¿Significa que las restricciones simplemente se nombran según la tabla anterior o que solo pertenecen a la tabla anterior (lo que significa que las restricciones se eliminan efectivamente)?
Maerics

El comentario antes de la creación de la tabla ( -- LOCK TABLE "table" IN ROW EXCLUSIVE mode;) parece ser insuficiente para protegerlo de una actualización / inserción en la tabla fuente de acuerdo con las especificaciones. Se ROW EXCLUSIVEpueden adquirir dos bloqueos sin ningún conflicto (consulte la Tabla 13.2 en postgresql.org/docs/10/explicit-locking.html#LOCKING-TABLES ). Para evitar actualizaciones de datos, necesita al menos un SHAREbloqueo.
Pilou
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.