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 잠금을 획득하므로 해당 트랜잭션이 커밋되거나 롤백 될 때까지 다른 사람이 테이블에서 읽을 수 없습니다.
Josh Kupershmidt

2
delete대신 사용하면 truncate속도가 느려지지만 독자를 차단하지는 않습니다. 몇 개의 행을 삭제해야합니까?
a_horse_with_no_name

@a_horse_with_no_name 일반적으로 varchar 열이 많은 200-300k 행 사이. 의 대기 시간 DELETE과는 INSERT너무 긴 것입니다.
Clarkey

답변:


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는 실제로 MySQL과 달리 트랜잭션 DDL을 지원하므로 위의 트랜잭션을 롤백 해야하는 경우 안전하게 할 수 있습니다.


귀하의 답변에 감사드립니다. LOCK TABLE제안한 방법을 사용한 경우 , 전에 다시 잠금을 해제해야 COMMIT합니까, 아니면 자체 잠금을 해제합니까?
Clarkey

1
편집 : 이 설명서 에서 다음과 같은 문구를 찾았습니다 . "UNLOCK TABLE 명령이 없습니다. 잠금은 항상 트랜잭션 끝에서 해제됩니다."
Clarkey

2
여기서 누락 된 한 가지는 여전히 속하는 모든 제약 조건입니다._old
Intellix

@Intellix 그것에 대해 자세히 설명해 주시겠습니까? 그것은 제약이 단순히 것을 의미 하는가 라는 이름의 이전 테이블 당하거나 것만 관련된 (제약 효과적으로 삭제됩니다 것을 의미) 이전 테이블에?
maerics

테이블 작성 이전의 주석 ( -- LOCK TABLE "table" IN ROW EXCLUSIVE mode;)이 스펙에 따라 소스 테이블의 업데이트 / 삽입으로부터 보호하기에 부족한 것 같습니다. ROW EXCLUSIVE충돌없이 두 개의 잠금을 확보 할 수 있습니다 ( postgresql.org/docs/10/explicit-locking.html#LOCKING-TABLES의 표 13.2 참조 ). 데이터 업데이트를 방지하려면 최소한 SHARE잠금 이 필요합니다 .
Pilou
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.