다음을 통해 공유


Transactions with Memory-Optimized Tables

적용 대상: SQL Server Azure SQL 데이터베이스 Azure SQL Managed Instance

이 문서에서는 메모리 최적화 테이블 및 고유하게 컴파일된 저장 프로시저와 관련된 트랜잭션의 모든 측면을 설명합니다.

SQL Server의 트랜잭션 격리 수준은 메모리 최적화 테이블과 디스크 기반 테이블에 다르게 적용되며 기본 메커니즘은 다릅니다. 차이점을 이해하면 프로그래머가 높은 처리량 시스템을 설계하는 데 도움이 됩니다. 트랜잭션 무결성의 목표는 모든 경우에 공유됩니다.

메모리 최적화 테이블의 트랜잭션에 대한 오류 조건을 보려면 충돌 검색 및 다시 시도 논리섹션을 참조하세요.

일반적인 정보는 SET TRANSACTION ISOLATION LEVEL(Transact-SQL)을 참조하세요.

비관적 및 낙관적

기능적 차이는 트랜잭션 무결성에 대한 비관적 접근 방식과 낙관적 접근 방식 때문입니다. 메모리 액세스에 최적화된 테이블은 낙관적 접근 방식을 사용합니다.

  • 비관적 접근 방식은 잠금을 사용하여 잠재적 충돌이 발생하기 전에 차단합니다. 문이 실행될 때 잠금이 수행되고 트랜잭션이 커밋될 때 해제됩니다.

  • 낙관적 접근 방식은 충돌이 발생할 때 감지하고 커밋 시 유효성 검사를 수행합니다.

    • 오류 1205 교착 상태는 메모리 최적화 테이블에 발생할 수 없습니다.

낙관적 접근 방식은 오버헤드가 적고 일반적으로 더 효율적입니다. 트랜잭션 충돌이 대부분의 애플리케이션에서 드물기 때문입니다. 비관적 접근 방식과 낙관적 접근 방식의 주요 기능적 차이점은 충돌이 발생하면 비관적 접근 방식에서 기다리는 반면 낙관적 접근 방식에서는 트랜잭션 중 하나가 실패하고 클라이언트가 다시 시도해야 한다는 것입니다. 기능 차이는 반복적 읽기 격리 수준이 적용되면 더 크고 직렬화 가능 수준에서 가장 큽니다.

트랜잭션 시작 모드

SQL Server에는 다음과 같은 트랜잭션 시작 모드가 있습니다.

  • 자동 커밋 - 단순 쿼리 또는 DML 문의 시작은 암시적으로 트랜잭션을 열고 문의 끝은 트랜잭션을 암시적으로 커밋합니다. 자동 커밋이 기본값입니다.

    • 자동 커밋 모드에서는 일반적으로 FROM 절의 메모리 최적화 테이블에서 트랜잭션 격리 수준에 대한 테이블 힌트를 코딩할 필요가 없습니다.
  • 명시적 - Transact-SQL에는 최종 COMMIT TRANSACTION과 함께 BEGIN TRANSACTION 코드가 포함됩니다. 두 개 이상의 문을 동일한 트랜잭션으로 내세울 수 있습니다.

    • 명시적 모드에서는 데이터베이스 옵션 MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT 사용하거나 FROM 절의 메모리 최적화 테이블에서 트랜잭션 격리 수준에 대한 테이블 힌트를 코딩해야 합니다.
  • 암시적 - SET IMPLICIT_TRANSACTION ON이 적용된 경우 이 옵션은 모두 0 = @@trancount인 경우 각 UPDATE 문 전에 명시적 BEGIN TRANSACTION에 해당하는 작업을 암시적으로 수행하기 때문에 IMPLICIT_BEGIN_TRANSACTION이 더 나은 이름일 수 있습니다. 따라서 결국 명시적 COMMIT TRANSACTION을 발급하는 것은 T-SQL 코드에 달려 있습니다.

  • ATOMIC BLOCK - ATOMIC 블록의 모든 문은 항상 단일 트랜잭션의 일부로 실행됩니다. Atomic 블록의 작업은 각각 전체로써 성공 시 커밋되거나 오류가 발생할 때 모두 롤백됩니다. 고유하게 컴파일된 저장 프로시저에는 각각 Atomic 블록이 필요합니다.

명시적 모드의 코드 예제

다음 해석된 TRANSACT-SQL 스크립트에서는 아래 항목을 사용합니다.

  • 명시적 트랜잭션.
  • dbo.Order_mo라는 메모리 최적화 테이블입니다.
  • READ COMMITTED 트랜잭션 격리 수준 컨텍스트입니다.

따라서 메모리 최적화 테이블에 테이블 힌트가 있어야 합니다. 힌트는 SNAPSHOT에 대한 것이거나 보다 더 격리된 수준이어야 합니다. 코드 예제의 경우 힌트는 WITH(SNAPSHOT)입니다. 이 힌트를 제거하면 스크립트에서 41368 오류가 발생하며 이 경우 자동화된 다시 시도는 적합하지 않습니다.

오류 41368

READ COMMITTED 격리 수준을 사용하여 메모리 최적화 테이블에 액세스하는 것은 자동 커밋 트랜잭션에 대해서만 지원됩니다. 명시적 또는 암시적 트랜잭션에는 지원되지 않습니다. WITH (SNAPSHOT) 같은 테이블 힌트를 사용하여 메모리 최적화 테이블에 대해 지원되는 격리 수준을 제공합니다.

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;  
GO  

BEGIN TRANSACTION;  -- Explicit transaction.  

-- Order_mo  is a memory-optimized table.  
SELECT * FROM  
           dbo.Order_mo  as o  WITH (SNAPSHOT)  -- Table hint.  
      JOIN dbo.Customer  as c  on c.CustomerId = o.CustomerId;  
COMMIT TRANSACTION;

데이터베이스 옵션 MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT을 사용할 경우 WITH (SNAPSHOT) 힌트를 사용하지 않아도 됩니다. 이 옵션이 ON으로 설정되면 더 낮은 격리 수준에서 메모리 최적화 테이블의 격리 수준이 자동으로 SNAPSHOT 격리로 승격됩니다.

ALTER DATABASE CURRENT
    SET MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT = ON;

행 버전 관리

메모리 최적화 테이블은 직렬화 가능의 가장 엄격한 격리 수준에서도 낙관적 접근 방식을 효율적으로 만드는 매우 정교한 행 버전 관리 시스템을 사용합니다. 자세한 내용은 메모리 액세스에 최적화된 테이블 소개를 참조하세요.

디스크 기반 테이블에는 READ_COMMITTED_SNAPSHOT 또는 SNAPSHOT 격리 수준이 적용되는 경우 행 버전 관리 시스템이 간접적으로 있습니다. 효율성을 최대로 하기 위해 이 시스템은 tempdb를 기반으로 하는 반면 메모리 최적화 데이터 구조에는 행 버전 관리가 기본 적용됩니다.

격리 수준

다음 표에서는 최소 격리에서 대부분까지 순서대로 가능한 트랜잭션 격리 수준을 나열합니다. 발생할 수 있는 충돌에 대한 자세한 내용과 이러한 충돌을 처리하기 위해 논리를 다시 시도하려면 충돌 검색 및 다시 시도 논리를 참조하세요.

격리 수준 설명
READ UNCOMMITTED 사용할 수 없음: 커밋되지 않은 격리에서는 메모리 최적화 테이블에 액세스할 수 없습니다. 세션 수준 TRANSACTION ISOLATION LEVEL이 READ UNCOMMITTED로 설정된 경우 WITH (SNAPSHOT) 테이블 힌트를 사용하거나 데이터베이스 설정 MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT을 ON으로 설정하여 SNAPSHOT 격리의 메모리 최적화 테이블에 액세스할 수 있습니다.
READ COMMITTED 자동 커밋 모드가 적용되는 경우에만 메모리 최적화 테이블에 대해 지원됩니다. 세션 수준 TRANSACTION ISOLATION LEVEL이 READ COMMITTED로 설정된 경우 WITH (SNAPSHOT) 테이블 힌트를 사용하거나 데이터베이스 설정 MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT을 ON으로 설정하여 SNAPSHOT 격리의 메모리 최적화 테이블에 액세스할 수 있습니다.

데이터베이스 옵션 READ_COMMITTED_SNAPSHOT이 켜기로 설정된 경우 동일한 문에서 READ COMMITTED 격리 하의 메모리 최적화 테이블 및 디스크 기반 테이블에 액세스할 수 없습니다.
SNAPSHOT 메모리 최적화 테이블에 지원됨.

내부적으로 SNAPSHOT은 메모리 최적화 테이블에 가장 덜 까다로운 트랜잭션 격리 수준입니다.

스냅샷은 반복적 읽기 또는 직렬화 가능보다 적은 시스템 리소스를 사용합니다.
REPEATABLE READ 메모리 최적화 테이블에 지원됨. 반복적 일기 격리에서 제공하는 보장은 커밋 시 동시 트랜잭션이 이 트랜잭션에서 읽은 행을 업데이트하지 않은 것입니다.

낙관적 모델 때문에 동시 트랜잭션은 이 트랜잭션에서 읽은 행을 업데이트할 수 없습니다. 대신 커밋 시 이 트랜잭션은 반복적 읽기 격리가 위반되지 않았다는 유효성을 검사했습니다. 있는 경우 이 트랜잭션은 롤백되며 다시 시도해야 합니다.
직렬화 가능 메모리 최적화 테이블에 지원됨.

격리가 너무 엄격하여 트랜잭션이 동시에 실행되지 않고 계열에서 실행되는 것과 거의 비슷하기 때문에 직렬화 가능이라는 이름이 지정됩니다.

트랜잭션 단계 및 수명

메모리 최적화 테이블이 관련되면 다음 이미지에 표시된 대로 트랜잭션의 수명이 단계를 진행합니다.

hekaton_transactions

단계에 대한 설명은 다음과 같습니다.

일반 처리: 1단계(총 3단계 중)

  • 이 단계는 모든 쿼리 및 쿼리의 DML 문 실행으로 구성됩니다.
  • 이 단계에서 문은 트랜잭션의 논리적 시작 시간을 기준으로 메모리 최적화 테이블의 버전을 확인합니다.

유효성 검사: 2단계(총 3단계 중)

  • 끝 시간이 지정되어 트랜잭션이 논리적으로 완료된 것으로 표시되면 유효성 검사 단계가 시작됩니다. 이렇게 하면 트랜잭션의 모든 변경 내용이 이 트랜잭션에 종속되는 다른 트랜잭션에 표시됩니다. 종속 트랜잭션은 이 트랜잭션이 성공적으로 커밋될 때까지 커밋할 수 없습니다. 또한 이러한 종속성을 갖는 트랜잭션에서는 결과 집합을 클라이언트로 반환할 수 없고 클라이언트에서는 데이터베이스에 성공적으로 커밋된 데이터만을 확인할 수 있습니다.
  • 이 단계는 반복적 읽기 및 직렬화 가능 유효성 검사로 구성됩니다. 반복적 읽기 유효성 검사에서는 트랜잭션에 의한 행 읽기가 업데이트되었는지 검사합니다. 직렬화 가능한 유효성 검사를 위해 이 트랜잭션에서 검사한 데이터 범위에 행이 삽입되었는지 여부를 확인합니다. 스냅샷 격리를 사용할 경우 격리 수준 및 충돌의 테이블당 반복적 읽기 및 직렬화 가능 유효성 검사가 발생하여 고유 및 외래 키 제약 조건의 일관성을 검사할 수 있습니다.

커밋 처리: 3단계(총 3단계 중)

  • 이 커밋 단계 동안 내구성이 있는 테이블의 변경 내용이 로그에 기록되고 로그는 디스크에 기록됩니다. 그런 다음 제어 권한이 클라이언트에 반환됩니다.
  • 커밋 처리가 완료되면 모든 종속 트랜잭션이 커밋할 수 있다는 알림이 표시됩니다.

언제나처럼 데이터 요구 사항에 유효한 것처럼 트랜잭션 작업 단위를 최소화하고 간략하게 유지해야 합니다.

충돌 검색 및 재시도 논리

트랜잭션 실패 및 롤백이 발생할 수 있는 트랜잭션 관련 오류 조건은 두 가지입니다. 대부분의 경우 이러한 오류가 발생하면 교착 상태가 발생할 때와 유사하게 트랜잭션을 다시 시도해야 합니다.

  • 동시 트랜잭션 간의 충돌. 이러한 오류는 업데이트 충돌 및 유효성 검사 실패이며 트랜잭션 격리 수준 위반 또는 제약 조건 위반으로 인해 발생할 수 있습니다.
  • 종속성 오류. 이는 의존하는 트랜잭션의 커밋이 실패하거나 종속 수가 너무 많이 늘어나서 발생하는 오류입니다.

메모리 최적화 테이블에 액세스할 때 트랜잭션이 실패할 수 있는 오류 조건은 다음과 같습니다.

오류 코드 설명 원인
41302 현재 트랜잭션이 시작된 이후 다른 트랜잭션에서 업데이트된 행을 업데이트하려고 했습니다. 이 오류 조건은 두 개의 동시 트랜잭션이 동시에 동일한 행을 업데이트하거나 삭제하려고 시도하는 경우에 발생합니다. 두 트랜잭션 중 하나는 이 오류 메시지를 수신하며 다시 시도해야 합니다.

41305 반복적인 읽기 유효성 검사 실패. 메모리 최적화 테이블에서 읽은 행은 이 트랜잭션을 커밋하기 전에 커밋된 다른 트랜잭션에 의해 업데이트되었습니다. 이 오류는 반복적 읽기 또는 직렬화 가능한 격리를 사용하는 경우와 동시 트랜잭션의 동작으로 인해 외래 키 제약 조건 위반이 발생하는 경우에도 발생할 수 있습니다.

이러한 외래 키 제약 조건의 동시 위반은 드물게 발생하며 일반적으로 애플리케이션 논리 또는 데이터 입력에 문제가 있다는 것을 나타냅니다. 그러나 외래 키 제약 조건과 관련된 열에 인덱스가 없는 경우에도 오류가 발생할 수 있습니다. 따라서 지침은 항상 메모리 최적화 테이블의 외래 키 열에 인덱스를 만드는 것입니다.

외래 키 위반으로 인한 유효성 검사 실패에 대한 자세한 고려 사항은 SQL Server 고객 자문 팀의 이 블로그 게시물을 참조하세요.
41325 직렬화 가능한 유효성 검사 실패. 새 행이 현재 트랜잭션에 의해 이전에 검색된 범위에 삽입되었습니다. 이를 가상 행이라고 부릅니다. 이 오류는 직렬화 가능한 격리를 사용하는 경우와 동시 트랜잭션의 동작으로 인해 기본 키, 고유 또는 외래 키 제약 조건 위반이 발생하는 경우에도 발생할 수 있습니다.

이러한 제약 조건의 동시 위반은 드물게 발생하며 일반적으로 애플리케이션 로직 또는 데이터 입력에 문제가 있다는 것을 나타냅니다. 그러나 반복적 읽기 유효성 검사 실패와 마찬가지로 관련 열에 인덱스가 없는 외래 키 제약 조건이 있는 경우에도 이 오류가 발생할 수 있습니다.
41301 종속성 실패: 나중에 커밋하지 못한 다른 트랜잭션에 종속성이 적용되었습니다. 이 트랜잭션(Tx1)은 다른 트랜잭션(Tx2)에서 작성한 데이터를 읽어 해당 트랜잭션(Tx2)이 유효성 검사 또는 커밋 처리 단계에 있는 동안 Tx2에 종속되었습니다. 이후 Tx2를 커밋하지 못했습니다. Tx2가 커밋에 실패하는 가장 일반적인 원인은 반복 읽기(41305) 및 직렬화(41325) 유효성 검사 실패이며 그 외에 로그 IO 실패 등이 있습니다.
4182341840 메모리 최적화 테이블 및 테이블 변수의 사용자 데이터 할당량에 도달했습니다. 오류 41823은 AZURE SQL 데이터베이스의 단일 데이터베이스 뿐만 아니라 SQL Server Express/웹/Standard Edition에도 적용됩니다. 오류 41840은 Azure SQL 데이터베이스의 Elastic Pool에 적용됩니다.

대부분의 경우 이러한 오류는 최대 사용자 데이터 크기에 도달했음을 나타내며 오류를 해결하는 방법은 메모리 최적화 테이블에서 데이터를 삭제하는 것입니다. 그러나 이 오류가 일시적인 경우는 드뭅니다. 따라서 이러한 오류가 처음 발생할 때 다시 시도하는 것이 좋습니다.

이 목록의 다른 오류와 마찬가지로 오류 41823 및 41840은 활성 트랜잭션이 중단됩니다.
41839 트랜잭션이 최대 커밋 종속성 수를 초과했습니다. 적용 대상: SQL Server 2016(13.x). 이후 버전의 SQL Server 및 Azure SQL 데이터베이스는 커밋 종속성 수에 제한이 없습니다.

특정 트랜잭션(Tx1)이 종속될 수 있는 트랜잭션의 수에는 제한이 있습니다. 이러한 트랜잭션은 나가는 종속성입니다. 또한 특정 트랜잭션(Tx1)에 종속될 수 있는 트랜잭션 수에도 제한이 있습니다. 이러한 트랜잭션은 들어오는 종속성입니다. 둘 다에 대한 제한은 8입니다.

이 오류는 일반적으로 단일 쓰기 트랜잭션에서 쓴 데이터에 액세스하는 읽기 트랜잭션 수가 많은 경우 발생합니다. 읽기 트랜잭션이 모두 동일한 데이터의 큰 검사를 수행하고 쓰기 트랜잭션의 유효성 검사 또는 커밋 처리가 오래 걸리는 경우(예: 쓰기 트랜잭션이 직렬화 가능한 격리(유효성 검사 단계의 길이 증가)에서 큰 검사를 수행하거나 트랜잭션 로그가 느린 로그 IO 디바이스에 배치되는(커밋 처리 길이 증가) 경우, 이 조건에 도달할 가능성이 높아집니다. 읽기 트랜잭션이 큰 검사를 수행하고 몇 개의 행에만 액세스해야 하는 경우 인덱스가 누락될 수 있습니다. 마찬가지로 쓰기 트랜잭션이 직렬화 가능한 격리를 사용하고 큰 검사를 수행하지만 몇 개의 행에만 액세스할 것으로 예상되는 경우 이는 누락된 인덱스의 표시이기도 합니다.

추적 플래그 9926을 사용하여 커밋 종속성 수에 대한 제한을 해제할 수 있습니다. 이 추적 플래그는 위에서 언급한 경우 이러한 문제를 가릴 수 있으므로 누락된 인덱스가 없음을 확인한 후에도 이 오류 조건에 도달한 경우에만 사용합니다. 또 다른 주의 사항은 복잡한 종속성 그래프입니다. 각 트랜잭션에 들어오는 종속성과 나가는 종속성이 많고 개별 트랜잭션에 많은 종속성 계층이 있으면 시스템의 효율성이 저하될 수 있습니다.

재시도 논리

위에서 언급한 조건 중 하나 때문에 트랜잭션이 실패하면 트랜잭션을 다시 시도해야 합니다.

클라이언트 또는 서버 쪽에서 재시도 논리를 구현할 수 있습니다. 일반적으로 클라이언트 쪽에서 재시도 논리를 구현하는 것이 더 효율적이며 오류가 발생하기 전에 트랜잭션에서 반환된 결과 집합을 처리할 수 있도록 하는 것이 좋습니다.

다시 시도 T-SQL 코드 예제

T-SQL을 사용하는 서버 쪽 재시도 논리는 결과 집합을 클라이언트에 반환하지 않는 트랜잭션에만 사용해야 합니다. 그렇지 않으면 재시도 시 예상된 결과 집합을 초과하여 클라이언트에 반환될 수 있습니다.

다음 해석된 T-SQL 스크립트는 메모리 최적화 테이블과 관련된 트랜잭션 충돌과 관련된 오류에 대해 재시도 논리가 어떻게 표시되는지 보여 줍니다.

-- Retry logic, in Transact-SQL.
DROP PROCEDURE If Exists usp_update_salesorder_dates;
GO

CREATE PROCEDURE usp_update_salesorder_dates
AS
BEGIN
    DECLARE @retry INT = 10;

    WHILE (@retry > 0)
    BEGIN
        BEGIN TRY
            BEGIN TRANSACTION;

            UPDATE dbo.SalesOrder_mo WITH (SNAPSHOT)
                set OrderDate = GetUtcDate()
                where CustomerId = 42;

            UPDATE dbo.SalesOrder_mo WITH (SNAPSHOT)
                set OrderDate = GetUtcDate()
                where CustomerId = 43;

            COMMIT TRANSACTION;

            SET @retry = 0;  -- //Stops the loop.
        END TRY

        BEGIN CATCH
            SET @retry -= 1;

            IF (@retry > 0 AND
                ERROR_NUMBER() in (41302, 41305, 41325, 41301, 41823, 41840, 41839, 1205)
                )
            BEGIN
                IF XACT_STATE() = -1
                    ROLLBACK TRANSACTION;

                WAITFOR DELAY '00:00:00.001';
            END
            ELSE
            BEGIN
                PRINT 'Suffered an error for which Retry is inappropriate.';
                THROW;
            END
        END CATCH

    END -- //While loop
END;
GO

--  EXECUTE usp_update_salesorder_dates;

컨테이너 간 트랜잭션

트랜잭션이 다음을 수행하는 경우 크로스 컨테이너 트랜잭션이라고 합니다.

  • 해석된 Transact-SQL에서 메모리 최적화 테이블에 액세스합니다. 또는
  • 트랜잭션이 이미 열려 있을 때 기본 프로시저를 실행하는 경우(XACT_STATE() = 1)

"크로스 컨테이너"라는 용어는 트랜잭션이 디스크 기반 테이블과 메모리 최적화 테이블에 대해 하나씩 두 개의 트랜잭션 관리 컨테이너에서 실행된다는 사실에서 파생됩니다.

단일 크로스 컨테이너 트랜잭션 내에서 디스크 기반 및 메모리 최적화 테이블에 액세스하기 위해 다른 격리 수준이 사용될 수 있습니다. 이 차이점은 WITH (SERIALIZABLE) 등의 명시적 테이블 힌트 또는 데이터베이스 옵션 MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT을 통해 표현됩니다. 이렇게 하면 TRANSACTION ISOLATION LEVEL이 READ COMMITTED 또는 READ UNCOMMITTED로 구성된 경우 메모리 최적화 테이블의 격리 수준이 명시적으로 스냅샷 수준으로 상승됩니다.

다음 Transact-SQL 코드 예제:

  • 디스크 기반 테이블 Table_D1은 READ COMMITTED 격리 수준을 사용하여 액세스됩니다.
  • 메모리 최적화 테이블 Table_MO7 직렬화 가능한 격리 수준을 사용하여 액세스합니다. 삽입은 항상 일관되고 기본적으로 직렬화 가능한 격리에서 실행되므로 Table_MO6 특정 관련 격리 수준이 없습니다.
-- Different isolation levels for
-- disk-based tables versus memory-optimized tables,
-- within one explicit transaction.

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
go

BEGIN TRANSACTION;

    -- Table_D1 is a traditional disk-based table, accessed using READ COMMITTED isolation.

    SELECT * FROM Table_D1;


    -- Table_MO6 and Table_MO7 are memory-optimized tables.
    -- Table_MO7 is accessed using SERIALIZABLE isolation,
    --   while Table_MO6 does not have a specific isolation level.

    INSERT Table_MO6
        SELECT * FROM Table_MO7 WITH (SERIALIZABLE);

COMMIT TRANSACTION;
go

제한 사항

  • 데이터베이스 간 트랜잭션은 메모리 최적화 테이블을 지원하지 않습니다. 트랜잭션이 메모리 최적화 테이블에 액세스하는 경우 다음을 제외하고는 다른 데이터베이스에 액세스할 수 없습니다.

    • tempdb 데이터베이스
    • master 데이터베이스에서 읽기 전용.
  • BEGIN DISTRIBUTED TRANSACTION이 사용되는 경우 분산 트랜잭션은 메모리 최적화 테이블에 액세스할 수 없습니다.

고유하게 컴파일된 저장 프로시저

  • 기본 프로시저에서 ATOMIC 블록은 다음과 같이 전체 블록에 대한 트랜잭션 격리 수준을 선언해야 합니다.

    • ... BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, ...) ...
  • 명시적 트랜잭션 제어 문은 기본 프로시저의 본문 내에서 허용되지 않습니다. BEGIN TRANSACTION, ROLLBACK TRANSACTION 등은 모두 허용되지 않습니다.

  • ATOMIC 블록을 사용하는 트랜잭션 제어 관련 자세한 내용은 Atomic 블록을 참조하세요.