직렬화 가능 격리 - Amazon Redshift

직렬화 가능 격리

일부 애플리케이션은 동시 쿼리 및 로드뿐 아니라 여러 테이블에 또는 같은 테이블 동시에 쓸 수 있는 능력이 필요합니다. 이 맥락에서 동시에는 정확히 같은 시간에 실행되도록 예약된 것을 말하는 것이 아니라 중첩된다는 뜻입니다. 첫 번째 트랜잭션이 커밋되기 전에 두 번째 트랜잭션이 시작된다면 두 트랜잭션은 동시적이라고 간주됩니다. 동시 작업은 같은 사용자 또는 서로 다른 사용자에 의해 제어되는 서로 다른 세션에서 비롯될 수 있습니다.

참고

Amazon Redshift는 별도로 실행되는 각각의 SQL 명령이 개별적으로 커밋되는 기본 자동 커밋 동작을 지원합니다. 명령 세트를 트랜잭션 블록(BEGINEND 문에 의해 정의됨)으로 묶는 경우, 이 블록은 하나의 트랜잭션으로 커밋되므로 필요할 경우, 롤백할 수 있습니다. 이 동작에 대한 예외가 TRUNCATE 및 VACUUM 명령으로서 현재 트랜잭션의 대기 중인 모든 변경을 자동으로 커밋합니다.

일부 SQL 클라이언트는 BEGIN 및 COMMIT 명령을 자동으로 실행하므로 클라이언트는 명령문 그룹이 트랜잭션으로 실행되는지 각 개별 명령문이 자체 트랜잭션으로 실행되는지 여부를 제어합니다. 사용 중인 인터페이스의 설명서를 확인하십시오. 예를 들어 Amazon Redshift JDBC 드라이버를 사용할 경우 세미콜론으로 구분된 여러 SQL 명령이 포함된 쿼리 문자열이 있는 JDBC PreparedStatement는 모든 명령문을 단일 트랜잭션으로 실행합니다. 반대로 SQL Workbench/J를 사용하고 AUTO COMMIT ON을 설정한 경우 여러 명령문을 실행하면 각 명령문이 자체 트랜잭션으로 실행됩니다.

Amazon Redshift에서 동시 쓰기 작업은 테이블에서의 쓰기 잠금 및 직렬화 가능 격리 원칙을 사용하여 보호적 방식으로 지원됩니다. 직렬화 가능 격리는 테이블에 대해 실행 중인 트랜잭션이 해당 테이블에 대해 실행 중인 유일한 트랜잭션이라는 환상을 보존합니다. 예를 들어 동시에 실행 중인 T1과 T2라는 2개의 트랜잭션은 적어도 다음 중 하나와 동일한 결과를 생성해야 합니다.

  • T1과 T2가 이 순서로 순차 실행됩니다.

  • T2와 T1이 이 순서로 순차 실행됩니다.

동시 트랜잭션은 서로에게 보이지 않으며, 서로의 변경을 감지할 수 없습니다. 각각의 동시 트랜잭션은 트랜잭션 시작 시 데이터베이스의 스냅샷을 생성합니다. 데이터베이스 스냅샷은 대부분의 SELECT 문과 COPY, DELETE, INSERT, UPDATE, TRUNCATE 같은 DML 명령 및 다음의 DDL 명령이 처음 발생할 때 트랜잭션 내에서 생성됩니다.

  • ALTER TABLE(열 추가 또는 삭제)

  • CREATE TABLE

  • DROP TABLE

  • TRUNCATE TABLE

동시 트랜잭션의 순차 실행 중 동시 실행과 동일한 결과를 생성하는 실행이 있는 경우 이러한 트랜잭션은 "직렬화 가능"한 것으로 간주되며 안전하게 실행할 수 있습니다. 동일한 결과를 생성할 수 있는 이러한 트랜잭션의 순차 실행이 없는 경우 직렬화 기능을 중단하는 문을 실행하는 트랜잭션은 중지되고 롤백됩니다.

시스템 카탈로그 테이블(PG)과 기타 Amazon Redshift 시스템 테이블(STL 및 STV)은 트랜잭션에서 잠기지 않습니다. 따라서 DDL 및 TRUNCATE 작업에서 발생하는 데이터베이스 객체의 변경 사항은 동시 트랜잭션에 대한 커밋 시 볼 수 있습니다.

예를 들어 2개의 동시 트랜잭션 T1과 T2가 시작될 때 데이터베이스에 테이블 A가 존재한다고 가정해 보십시오. T2가 PG_TABLES 카탈로그 테이블에서 선택하여 테이블 목록을 반환한다고 가정합니다. 그런 다음 T1이 테이블 A를 삭제하고 커밋한 다음 T2가 테이블을 다시 나열합니다. 이제 테이블 A가 더 이상 나열되지 않습니다. T2가 삭제된 테이블을 쿼리하려고 하는 경우 Amazon Redshift는 "관계가 존재하지 않음(relation does not exist)" 오류를 반환합니다. 테이블 목록을 T2에 반환하거나 테이블 A가 존재하는지 확인하는 카탈로그 쿼리에는 사용자 테이블에서 수행되는 작업과 동일한 격리 규칙이 적용되지 않습니다.

이런 테이블의 업데이트를 위한 트랜잭션은 읽기 커밋된 격리 모드에서 실행됩니다. PG-prefix 카탈로그 테이블은 스냅샷 격리를 지원하지 않습니다.

시스템 테이블과 카탈로그 테이블의 직렬화 가능 격리

데이터베이스 스냅샷은 사용자가 생성한 테이블 또는 Amazon Redshift 시스템 테이블(STL 또는 STV)을 참조하는 SELECT 쿼리의 트랜잭션에서도 생성됩니다. 테이블을 참조하지 않는 SELECT 쿼리는 새 트랜잭션 데이터베이스 스냅샷을 생성하지 않습니다. 시스템 카탈로그 테이블(PG)에서만 작동하는 INSERT, DELETE 및 UPDATE 문도 새 트랜잭션 데이터베이스 스냅샷을 생성하지 않습니다.

직렬화 가능 격리 오류 수정 방법

ERROR:1023 DETAIL: Redshift의 테이블에서 직렬화 가능한 격리 위반

Amazon Redshift에서 직렬화 가능 격리 오류를 감지하면 다음과 같은 오류 메시지가 표시됩니다.

ERROR:1023 DETAIL: Serializable isolation violation on table in Redshift

직렬화 가능 격리 오류를 해결하기 위해 다음과 같은 방법을 시도할 수 있습니다.

  • 취소된 트랜잭션을 재시도합니다.

    Amazon Redshift에서 동시 워크로드를 직렬화할 수 없음을 감지했습니다. 이는 일반적으로 오류가 발생한 트랜잭션을 재시도하여 해결할 수 있는 애플리케이션 논리의 차이를 나타냅니다. 문제가 지속되면 다른 방법 중 하나를 시도하십시오.

  • 동일한 원자성 트랜잭션에 있을 필요가 없는 모든 작업을 트랜잭션 외부로 이동합니다.

    이 방법은 두 개의 트랜잭션 내에 있는 개별 작업이 다른 트랜잭션의 결과에 영향을 미칠 수 있는 방식으로 상호 참조하는 경우에 사용할 수 있습니다. 예를 들어 다음 두 세션은 각자 트랜잭션을 시작합니다.

    Session1_Redshift=# begin;
    Session2_Redshift=# begin;

    각 트랜잭션에 있는 SELECT 문의 결과는 다른 트랜잭션에 있는 INSERT 문에 영향을 받을 수 있습니다. 다시 말해 다음과 같은 문을 어떤 순서로든 직렬 방식으로 실행한다고 가정합시다. 어떤 경우에도 트랜잭션이 동시에 실행된 경우보다 한 행을 더 반환하는 SELECT 문 중 하나를 얻게 됩니다. 작업을 직렬로 실행하여 동시에 실행할 때와 동일한 결과를 산출할 수 있는 순서는 없습니다. 따라서 마지막으로 실행되는 작업으로 인해 직렬화 가능 격리 오류가 발생합니다.

    Session1_Redshift=# select * from tab1; Session1_Redshift=# insert into tab2 values (1);
    Session2_Redshift=# insert into tab1 values (1); Session2_Redshift=# select * from tab2;

    많은 경우 SELECT 문의 결과는 중요하지 않습니다. 다시 말해 트랜잭션 내 작업의 원자성은 중요하지 않습니다. 이러한 경우에는 다음 예와 같이 SELECT 문을 트랜잭션 외부로 이동합니다.

    Session1_Redshift=# begin; Session1_Redshift=# insert into tab1 values (1) Session1_Redshift=# end; Session1_Redshift=# select * from tab2;
    Session2_Redshift # select * from tab1; Session2_Redshift=# begin; Session2_Redshift=# insert into tab2 values (1) Session2_Redshift=# end;

    이 예의 경우 트랜잭션 내 교차 참조는 없습니다. 두 가지 INSERT 문은 서로에게 영향을 미치지 않습니다. 이 예의 경우 트랜잭션이 직렬로 실행되어 동시에 실행될 때와 동일한 결과를 산출하는 순서가 최소 한 개 있습니다. 이는 트랜잭션을 직렬화할 수 있음을 뜻합니다.

  • 각 세션에서 모든 테이블을 잠가 직렬화를 강제 실행하십시오.

    LOCK 명령은 직렬화 가능 격리 오류를 발생시킬 수 있는 작업을 차단합니다. LOCK 명령 사용 시 다음을 반드시 수행하십시오.

    • 트랜잭션 내 읽기 전용 SELECT 문의 영향을 받는 테이블을 포함해 트랜잭션의 영향을 받는 모든 테이블을 잠급니다.

    • 작업이 수행되는 순서에 상관없이 테이블을 동일한 순서로 잠급니다.

    • 작업을 수행하기 전에 트랜잭션을 시작하는 시점에 모든 테이블을 잠급니다.

  • 동시 트랜잭션에는 스냅샷 격리를 사용하세요.

    ALTER DATABASE 명령을 스냅샷 격리와 함께 사용하십시오. ALTER DATABASE의 SNAPSHOT 파라미터에 대한 자세한 내용은 파라미터 섹션을 참조하세요.

ERROR:1018 DETAIL: 관계가 존재하지 않음

여러 세션에서 동시 Amazon Redshift 작업을 실행하면 다음과 같은 오류 메시지가 표시됩니다.

ERROR: 1018 DETAIL: Relation does not exist.

Amazon Redshift의 트랜잭션은 스냅샷 격리를 따릅니다. 트랜잭션이 시작된 후 Amazon Redshift는 데이터베이스의 스냅샷을 만듭니다. 트랜잭션의 전체 수명 주기 동안 트랜잭션은 스냅샷에 반영된 데이터베이스 상태에서 작동합니다. 트랜잭션이 스냅샷에 존재하지 않는 테이블에서 읽는 경우 이전에 표시된 1018 오류 메시지가 나타납니다. 트랜잭션이 스냅샷을 만든 후 다른 동시 트랜잭션이 테이블을 생성하더라도 트랜잭션은 새로 생성된 테이블에서 읽을 수 없습니다.

이 직렬화 격리 오류를 해결하기 위해 테이블이 존재함을 알고 있는 시점으로 트랜잭션 시작을 이동하려고 할 수 있습니다.

테이블이 다른 트랜잭션에 의해 생성된 경우 이 시점은 적어도 해당 트랜잭션이 커밋된 이후입니다. 또한 테이블을 삭제했을 수 있는 동시 트랜잭션이 커밋되지 않았는지 확인합니다.

session1 = # BEGIN; session1 = # DROP TABLE A; session1 = # COMMIT;
session2 = # BEGIN;
session3 = # BEGIN; session3 = # CREATE TABLE A (id INT); session3 = # COMMIT;
session2 = # SELECT * FROM A;

session2에 의해 읽기 작업으로 실행되는 마지막 작업이 직렬화 가능한 격리 오류를 발생시킵니다. 이 오류는 session2가 스냅샷을 만들고 테이블이 커밋된 session1에 의해 이미 삭제된 경우 발생합니다. 즉, 동시 session3이 테이블을 생성하더라도 session2는 테이블이 스냅샷에 없기 때문에 테이블을 볼 수 없습니다.

이 오류를 해결하기 위해 다음과 같이 세션을 재정렬할 수 있습니다.

session1 = # BEGIN; session1 = # DROP TABLE A; session1 = # COMMIT;
session3 = # BEGIN; session3 = # CREATE TABLE A (id INT); session3 = # COMMIT;
session2 = # BEGIN; session2 = # SELECT * FROM A;

이제 session2가 스냅샷을 만들 때 session3은 이미 커밋되었으며 테이블은 데이터베이스에 있습니다. Session2는 오류 없이 테이블에서 읽을 수 있습니다.