다음을 통해 공유


자습서: 테이블 간 트랜잭션 조정

중요합니다

Unity 카탈로그 관리 델타 테이블에 쓰는 트랜잭션은 공개 미리 보기로 제공됩니다.

Unity 카탈로그 관리되는 Iceberg 테이블에 쓰는 트랜잭션은 프라이빗 미리 보기로 제공됩니다. 이 미리 보기에 참가하려면 관리되는 Iceberg 테이블 미리 보기 등록 양식을 제출합니다.

이 자습서에서는 트랜잭션을 사용하여 여러 문 및 테이블에서 업데이트를 조정하는 방법을 보여 줍니다. 자동으로 커밋되는 비대화형 트랜잭션과 명시적 제어를 제공하는 대화형 트랜잭션의 트랜잭션 모드를 모두 알아봅니다. 또한 이 자습서에서는 저장 프로시저 및 SQL 스크립팅과 함께 트랜잭션을 사용하여 Azure Databricks에서 중요 업무용 웨어하우징 워크로드를 빌드하는 방법을 보여 줍니다.

요구 사항

  • 환경: Azure Databricks 작업 영역에 액세스합니다.
  • 컴퓨팅: 지원되는 컴퓨팅 유형은 트랜잭션 모드에 따라 다릅니다.
    • 클래식 또는 서버리스 SQL 웨어하우스 는 두 트랜잭션 모드를 모두 지원합니다.
    • 서버리스 컴퓨팅 은 비대화형 트랜잭션만 지원합니다.
    • Databricks Runtime 18.0 이상을 실행하는 클래식 클러스터 는 비대화형 트랜잭션만 지원합니다.
  • 권한: CREATE TABLEUnity 카탈로그 스키마 내에서.

샘플 테이블 설정

다중 문으로 작성된 모든 테이블, 다중 테이블 트랜잭션은 다음을 수행해야 합니다.

SQL 편집기 또는 Notebook에서 다음 두 개의 샘플 테이블을 만듭니다.

-- Create a table for account data
CREATE TABLE IF NOT EXISTS sample_accounts (
  id INT,
  account_name STRING,
  balance DECIMAL(10,2)
) USING DELTA
TBLPROPERTIES (
  'delta.feature.catalogManaged' = 'supported'
);

-- Create a table for transaction records
CREATE TABLE IF NOT EXISTS sample_transactions (
  id INT,
  account_id INT,
  transaction_type STRING,
  amount DECIMAL(10,2)
) USING DELTA
TBLPROPERTIES (
  'delta.feature.catalogManaged' = 'supported'
);

-- To upgrade an existing table, use:
-- ALTER TABLE <table_name> SET TBLPROPERTIES ('delta.feature.catalogManaged' = 'supported');

-- Insert sample data
INSERT INTO sample_accounts VALUES
  (1, 'Alice', 1000.00),
  (2, 'Bob', 500.00);

INSERT INTO sample_transactions VALUES
  (1, 1, 'deposit', 100.00);

설치를 확인합니다.

SELECT * FROM sample_accounts;
SELECT * FROM sample_transactions;

출력:

sample_accounts:
id	account_name	balance
1	  Alice	        1000.00
2	  Bob	          500.00

sample_transactions:
id	account_id	transaction_type	amount
1	           1	         deposit	100.00

비대화형 트랜잭션

비대화형 트랜잭션은 BEGIN ATOMIC ... END; 구문을 사용합니다. 모든 문은 단일 원자 단위로 실행됩니다. 모든 명령문이 성공하면 Azure Databricks가 자동으로 커밋합니다. 문장이 실패하면 Azure Databricks는 모든 변경 내용을 자동으로 되돌립니다. 자세한 구문 및 사용 패턴은 비대화형 트랜잭션을 참조하세요.

성공적인 트랜잭션 실행

두 테이블을 동시에 원자적으로 업데이트합니다.

BEGIN ATOMIC
  -- Update Alice's account balance
  UPDATE sample_accounts
  SET balance = balance + 100.00
  WHERE id = 1;

  -- Record the deposit transaction
  INSERT INTO sample_transactions
  VALUES (2, 1, 'deposit', 100.00);
END;

두 작업이 모두 성공했는지 확인합니다.

-- Alice's balance should now be 1100.00
SELECT * FROM sample_accounts WHERE id = 1;

-- Should show two transaction records
SELECT * FROM sample_transactions;

잔액 업데이트와 트랜잭션 레코드가 모두 함께 만들어졌습니다. 두 문 중 하나가 실패한 경우 두 변경 내용이 모두 커밋되지 않았을 것이며 Databricks는 부작용 없이 트랜잭션을 종료했을 것입니다.

조건에 따라 SIGNAL을 사용하여 트랜잭션을 실패시키기

BEGIN ATOMIC ... END; 블록 내에서 SIGNAL를 사용하여 사용자 정의 조건이 충족되지 않을 때 트랜잭션을 실패하도록 할 수 있습니다. 커밋하기 전에 데이터 유효성 검사에 유용합니다.

BEGIN ATOMIC
  -- Insert new account
  INSERT INTO sample_accounts VALUES (3, 'Charlie', -50.00);

  -- Fail the transaction if balance is negative
  IF (SELECT balance FROM sample_accounts WHERE id = 3) < 0 THEN
    SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Account balance cannot be negative';
  END IF;
END;

오류가 SIGNAL 발생하여 전체 트랜잭션이 자동으로 롤백됩니다. 삽입이 롤백되었는지 검증하십시오.

-- Should return 0 rows (the transaction was rolled back by SIGNAL)
SELECT * FROM sample_accounts WHERE id = 3;

실패 시 자동 롤백 참조

잘못된 명령문을 사용하여 트랜잭션을 실행합니다.

BEGIN ATOMIC
  -- This statement is valid
  INSERT INTO sample_accounts VALUES (4, 'David', 300.00);

  -- This statement will fail (table does not exist)
  INSERT INTO non_existent_table VALUES (1, 2, 3);
END;

오류가 발생하여 트랜잭션이 실패합니다. 첫 번째 명령문이 롤백되었는지 확인합니다.

-- Should return 0 rows because the transaction was rolled back
SELECT * FROM sample_accounts WHERE id = 4;

첫 번째 INSERT 문이 유효했지만 두 번째 문이 실패하여 롤백되었습니다. 트랜잭션의 전부 또는 전혀 보장하지 않음을 보여 줍니다.

대화형 트랜잭션

대화형 트랜잭션을 사용하면 커밋 또는 롤백할 시기를 명시적으로 제어할 수 있습니다. BEGIN TRANSACTION을 사용하여 시작한 다음 COMMIT을 사용하여 변경 내용을 저장하거나 ROLLBACK을 사용하여 삭제합니다.

변경 내용 커밋

트랜잭션 시작:

BEGIN TRANSACTION;

변경(아직 커밋되지 않음):

INSERT INTO sample_accounts VALUES (5, 'Eve', 850.00);
UPDATE sample_accounts SET balance = balance + 50.00 WHERE id = 2;

변경 사항을 영구적으로 적용하려면 커밋하십시오.

COMMIT;

변경 내용을 확인합니다.

-- Eve's account should now be visible
SELECT * FROM sample_accounts WHERE id = 5;

-- Bob's balance should be 550.00 (500 + 50)
SELECT * FROM sample_accounts WHERE id = 2;

변경 취소

새 트랜잭션을 시작합니다.

BEGIN TRANSACTION;

변화를 만드세요.

INSERT INTO sample_accounts VALUES (6, 'Frank', 600.00);

변경 내용이 세션에 표시되는지 확인합니다.

-- Should show Frank's account (visible in your session only)
SELECT * FROM sample_accounts WHERE id = 6;

변경 내용을 취소하려면 롤백합니다.

ROLLBACK;

변경 내용이 삭제되었는지 확인합니다.

-- Should return 0 rows (the insert was rolled back)
SELECT * FROM sample_accounts WHERE id = 6;

저장 프로시저 및 SQL 스크립팅과 함께 사용

트랜잭션을 저장 프로시저 와 결합하여 재사용 가능한 트랜잭션 논리를 만들 수 있습니다. 이 패턴은 자주 실행하는 복잡한 작업에 유용합니다.

  1. 카탈로그 관리 커밋을 사용하도록 설정된 두 개의 테이블 만들기

    
    CREATE TABLE orders (order_id STRING, item_sku STRING, quantity_ordered INT)
     TBLPROPERTIES ('delta.feature.catalogManaged' = 'supported');
    
    CREATE TABLE inventory (item_sku STRING, quantity_in_stock INT)
     TBLPROPERTIES ('delta.feature.catalogManaged' = 'supported');
    
    
  2. 저장 프로시저 정의

    CREATE OR REPLACE PROCEDURE main.retail.apply_order(
        IN  p_order_id      STRING,
        IN  p_customer_id   STRING,
        IN  p_order_amount  DECIMAL(18,2)
    )
    LANGUAGE SQL
    SQL SECURITY INVOKER
    MODIFIES SQL DATA
    AS
    BEGIN
        -- Insert the order
        INSERT INTO main.retail.orders (order_id, customer_id, amount)
        VALUES (p_order_id, p_customer_id, p_order_amount);
    
        -- Update total sales per customer
        MERGE INTO main.retail.total_sales AS t
        USING (
            SELECT
              p_customer_id  AS customer_id,
              p_order_amount AS order_amount
        ) s
          ON t.customer_id = s.customer_id
        WHEN MATCHED THEN
          UPDATE SET t.total_amount = t.total_amount + s.order_amount
        WHEN NOT MATCHED THEN
          INSERT (customer_id, total_amount)
          VALUES (s.customer_id, s.order_amount);
    END;
    
    
  3. 트랜잭션 정의

    BEGIN ATOMIC
        -- Staging batch id for this transaction
        DECLARE new_order_id STRING DEFAULT uuid();
        DECLARE v_batch_id STRING DEFAULT uuid();
    
        -- 1) Stage incoming customer and order rows
        INSERT INTO main.retail.orders_staging (order_id, customer_id, amount, batch_id)
        VALUES (new_order_id, 'CUST_123', 249.99, v_batch_id);
    
        -- 2) Drive final writes from staging to production via stored procedure
        FOR o AS
          SELECT
            order_id,
            customer_id,
            amount
          FROM main.retail.orders_staging
          WHERE batch_id = v_batch_id
        DO
            CALL main.retail.apply_order(
              o.order_id,
              o.customer_id,
              o.amount
            );
        END FOR;
    
        -- 3) Clean up processed staging rows
        DELETE FROM main.retail.orders_staging
        WHERE batch_id = v_batch_id;
    END; -- 4) Commit the transaction
    
    

트랜잭션의 일부가 실패하면 Databricks는 모든 변경 내용을 자동으로 롤백합니다.

정리하다

샘플 테이블을 제거합니다.

DROP TABLE IF EXISTS sample_accounts;
DROP TABLE IF EXISTS sample_transactions;


DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS inventory;

다음 단계