Openflow Connector for SQL Server

Openflow Connector for SQL Server (CDC) 설정 (Set up)

이 페이지에서는 Openflow Connector for SQL Server (CDC)를 설정하는 방법을 설명해요. SQL Server 인스턴스 준비, CDC 활성화, Snowflake 환경 설정, 커넥터 설치·구성까지 전체 절차를 다룹니다.

출처: 문서

본문

기능 — 일반 공급 (Generally Available)

Snowflake 커넥터는 Snowflake Openflow를 사용할 수 있는 모든 리전에서 지원돼요.

  • Openflow Snowflake 배포는 AWS, Azure, GCP Commercial 리전의 모든 계정에서 사용할 수 있어요.
  • BYOC 배포의 Snowflake Openflow는 AWS Commercial 리전의 모든 계정에서만 사용할 수 있어요.

참고

이 커넥터는 Snowflake Connector 약관이 적용돼요.

이 항목에서는 Openflow Connector for SQL Server (CDC)를 설정하는 방법을 설명해요. 증분 로드 과정에 대한 정보는 증분 복제를 참고해요.

사전 요구 사항 (Prerequisites)

커넥터를 설정하기 전에 다음 사전 요구 사항을 완료했는지 확인해요.

  • Openflow Connector for SQL Server (CDC) 소개를 검토했는지 확인해요.
  • 지원되는 SQL Server 버전을 검토했는지 확인해요.
  • 런타임 배포를 설정했는지 확인해요. 자세한 내용은 다음 항목을 참고해요.
    • Openflow - Snowflake 배포 설정
    • Openflow - BYOC 설정
  • Openflow - Snowflake 배포를 사용한다면 필수 도메인 구성을 검토하고 SQL Server 커넥터에 필요한 도메인에 접근 권한을 부여했는지 확인해요.

SQL Server 인스턴스 설정 (Set up your SQL Server instance)

커넥터를 설정하기 전에 SQL Server 환경에서 다음 작업을 수행해요.

참고

이 작업들은 데이터베이스 관리자(database administrator)로 수행해야 해요.

  • 복제할 데이터베이스와 테이블에서 변경 데이터 캡처(Change Data Capture, CDC)를 활성화해요.

    USE <database>;
    EXEC sys.sp_cdc_enable_db;
    
    EXEC sys.sp_cdc_enable_table
      @source_schema = N'<schema>',
      @source_name = N'<table>',
      @role_name = NULL;
    

    참고

    복제할 모든 테이블에 대해 sp_cdc_enable_table 프로시저를 실행해요. sp_cdc_enable_db는 데이터베이스당 한 번 실행해요.

    커넥터는 복제가 시작되기 전에 데이터베이스와 테이블에서 CDC가 활성화되어 있어야 해요. 커넥터가 실행되는 동안 추가 테이블에서도 CDC를 활성화할 수 있어요.

    참고

    데이터베이스 수준 CDC 활성화에는 플랫폼별 변형이 있어요. 위에 표시된 sp_cdc_enable_table 호출은 모든 플랫폼에서 동일하며, 데이터베이스 수준 활성화 프로시저만 다릅니다.

    • AWS RDS for SQL Server: RDS는 sysadmin 서버 역할을 노출하지 않으므로 RDS에서 sys.sp_cdc_enable_db를 직접 호출할 수 없어요. RDS가 제공하는 래퍼(wrapper)를 대신 사용해요.

      EXEC msdb.dbo.rds_cdc_enable_db '<database>';
      

      Amazon RDS for SQL Server용 변경 데이터 캡처 사용을 참고해요. CDC는 RDS for SQL Server의 Web 에디션에서는 지원되지 않아요.

    • Google Cloud SQL for SQL Server: sys.sp_cdc_enable_db를 직접 호출할 수 없어요. Cloud SQL이 제공하는 래퍼를 대신 사용해요.

      EXEC msdb.dbo.gcloudsql_cdc_enable_db '<database>';
      

      Cloud SQL for SQL Server에서 변경 데이터 캡처(CDC) 활성화를 참고해요. Cloud SQL for SQL Server는 현재 SQL Server 2017, 2019, 2022만 제공해요.

    • Azure SQL Database (단일 데이터베이스): 표준 sys.sp_cdc_enable_db 프로시저를 사용해요. DTU 기반 구매 모델에서는 CDC에 S3 서비스 계층 이상이 필요해요(Basic, S0, S1, S2에서는 지원되지 않음). vCore 기반 구매 모델에서는 모든 계층에서 CDC가 지원돼요. Azure SQL Database의 변경 데이터 캡처를 참고해요.

    • Azure SQL Managed Instance: 표준 sys.sp_cdc_enable_db 프로시저를 사용해요. CDC 활성화에는 sysadmin 서버 역할의 구성원 자격이 필요해요.

  • 큰 LOB 열에 대한 max text repl size 높이기: 복제된 테이블에 64 KB보다 큰 값이 있는 LOB 열(예: VARCHAR(MAX), NVARCHAR(MAX), VARBINARY(MAX))이 포함되어 있다면, 소스 인스턴스에서 SQL Server max text repl size 설정을 높여요. SQL Server CDC는 이 설정을 기본적으로 65536바이트(64 KB)로 두는데, 이는 커넥터의 16 MB 값별 한도보다 낮아요. 높이지 않으면 다음과 같은 오류로 복제가 실패할 수 있어요.

    Length of LOB data (N) to be replicated exceeds configured maximum 65536. Use the stored procedure sp_configure to increase the configured maximum value for max text repl size option.
    

    소스 데이터의 가장 큰 LOB 크기에 따라 값을 설정해요. SQL Server CDC가 복제해야 하는 가장 큰 값만큼은 커져야 해요. Oversized Value Strategy가 Set Null로 설정된 경우에도 마찬가지예요(커넥터는 값을 NULL로 대체하기 전에 전체 값을 여전히 읽어요).

    경고

    max text repl size를 높이면 SQL Server CDC가 더 큰 LOB 값을 캡처할 수 있지만, 그 값들은 트랜잭션 로그에 기록되고 CDC 변경 테이블로 복사돼요. 매우 큰 값을 캡처하면 CDC 캡처 및 정리 작업에서 트랜잭션 로그 생성, 스토리지 소비, 지연이 증가할 수 있어요. 그 여유(headroom)가 필요하지 않다면 최대값이 아니라 실제로 필요한 LOB 크기에 맞게 한도를 설정해요.

    설정 변경 방법은 플랫폼에 따라 달라요.

    • 온프레미스 SQL Server, Azure SQL Managed Instance, Google Cloud SQL for SQL Server: sp_configure를 사용해요. 아래 예시는 허용되는 최대값인 2147483647(약 2 GB)을 설정해요.

      EXEC sp_configure 'show advanced options', 1;
      RECONFIGURE;
      EXEC sp_configure 'max text repl size', 2147483647;
      RECONFIGURE;
      

      자세한 내용은 max text repl size 서버 구성 옵션 구성을 참고해요.

    • AWS RDS for SQL Server: 이 설정을 sp_configure나 msdb.dbo.rds_set_configuration으로 변경할 수 없어요. RDS DB 파라미터 그룹을 통해 구성해요.

      • SQL Server 버전 제품군용 사용자 지정 DB 파라미터 그룹을 만들어요(예: sqlserver-se-16.0).

        aws rds create-db-parameter-group \
          --db-parameter-group-name <name> \
          --db-parameter-group-family sqlserver-se-16.0 \
          --description "Custom SQL Server params"
        
      • max text repl size (b)를 설정해요(정확한 파라미터 이름이 소문자로 (b)를 포함함에 주의). 아래 예시는 허용되는 최대값인 2147483647(약 2 GB)을 사용해요.

        aws rds modify-db-parameter-group \
          --db-parameter-group-name <name> \
          --parameters "ParameterName='max text repl size (b)',ParameterValue=2147483647,ApplyMethod=immediate"
        
      • 파라미터 그룹을 RDS 인스턴스에 연결해요.

        aws rds modify-db-instance \
          --db-instance-identifier <instance-id> \
          --db-parameter-group-name <name> \
          --apply-immediately
        
      • RDS 인스턴스를 재부팅해요. RDS for SQL Server에서 이 파라미터는 적용되려면 재부팅이 필요해요.

    • Azure SQL Database (단일 데이터베이스): 특정 데이터베이스에 연결된 쿼리 창을 열고 다음을 실행해요.

      EXEC sp_configure 'max text repl size', 2147483647;
      RECONFIGURE;
      

      -1 값도 지원되며, 열 데이터 타입이 부과하는 한도를 제외한 크기 한도를 제거해요.

  • SQL Server 인스턴스용 로그인을 만들어요.

    CREATE LOGIN <user_name> WITH PASSWORD = '<password>';
    

    이 로그인은 복제할 데이터베이스의 사용자를 만드는 데 사용돼요.

  • 복제하는 각 데이터베이스에서 다음 SQL Server 명령을 실행해 사용자를 만들어요.

    USE <source_database>;
    CREATE USER <user_name> FOR LOGIN <user_name>;
    
  • 복제하는 각 데이터베이스의 사용자에게 필요한 권한을 부여해요. 커넥터가 소스 테이블과 CDC 변경 테이블 둘 다 읽을 수 있도록 사용자를 db_datareader 역할에 추가하고 cdc 스키마에 대한 SELECT를 부여해요.

    ALTER ROLE db_datareader ADD MEMBER <user_name>;
    GRANT SELECT ON SCHEMA::cdc TO <user_name>;
    

    복제할 각 데이터베이스에서 이 명령들을 실행해요.

    참고

    이러한 권한은 커넥터에게 데이터베이스의 모든 사용자 테이블에 대한 읽기 접근을 부여해요. 접근을 더 좁게 범위를 지정하려면, 사용자를 db_datareader 역할에 추가하는 대신 복제되는 특정 테이블과 SCHEMA::cdc에만 SELECT를 부여해요.

    참고

    Azure SQL Database(단일 데이터베이스)만 — 래퍼 스크립트 배포 전 데이터베이스 소유자: 래퍼 프로시저는 EXECUTE AS OWNER를 사용해요. 데이터베이스 소유자가 Microsoft Entra ID 보안 주체인 경우(.bacpac 파일에서 데이터베이스를 가져온 뒤 흔함) 커넥터가 SQL Server 인증으로 인증하면 dbo.sf_openflow_cdc_enable_table과 dbo.sf_openflow_cdc_disable_table 호출이 "Only active directory users can impersonate other active directory users" 같은 오류(오류 33171)로 실패해요. 커넥터는 이 단계에서 추가 권한을 받지 않아요. 이 단계는 데이터베이스의 소유자만 변경해요.

    5단계에서 Openflow CDC 래퍼 스크립트를 배포하기 전에, 서버 관리자로 각 복제 데이터베이스에 연결해 다음을 실행해요.

    ALTER AUTHORIZATION ON DATABASE::[<database>] TO [<sql_server_admin_login>];
    

    논리 서버를 관리하는 SQL Server 인증 로그인(예: 서버를 만들 때 지정한 로그인)을 사용해요. 커넥터 로그인은 사용하지 마세요.

  • 커넥터가 지원되는 스키마 변경 시 캡처 인스턴스를 순환(rotate)할 수 있도록 Openflow CDC 래퍼 프로시저를 배포해요. 배포 지침은 Openflow CDC 래퍼 프로시저 배포를 참고해요. 권한이나 내부 정책으로 배포가 막히면 래퍼 프로시저가 배포되지 않은 경우를 참고해요.

  • (선택 사항) 사용자 정의 데이터 타입(UDDT)에 대한 VIEW DEFINITION 권한을 부여해요. 테이블에 UDDT를 사용하는 열이 있고 UDDT가 커넥터 사용자와 다른 사용자가 소유한 경우, 다음 SQL Server 예시처럼 커넥터 사용자에게 VIEW DEFINITION 권한을 부여해야 해요.

    GRANT VIEW DEFINITION TO <user_name>;
    

    이 권한이 없으면 UDDT를 사용하는 열은 복제에서 조용히 제외돼요.

  • (선택 사항) SSL 연결을 구성해요. SSL 연결로 SQL Server에 연결한다면 데이터베이스 서버의 루트 인증서를 만들어요. 이는 커넥터 구성 시 필요해요.

Openflow CDC 래퍼 프로시저 배포 (Deploy the Openflow CDC wrapper procedures)

참고

커넥터 설정 중(5단계) 이 프로시저들을 배포해요. 배포하지 않으면 지원되는 스키마 변경이 발생할 때까지 복제는 정상적으로 실행돼요. 그 시점에 DBA가 각 변경에 대해 MultiDatabaseCaptureChangeCdcSqlServer 프로세서의 WARN 게시판에서 SQL을 실행해야 해요. 권한이나 내부 정책으로 배포가 막히는 경우에만 그 경로를 택해요. 확인할 사항은 래퍼 프로시저가 배포되지 않은 경우를 참고해요.

커넥터는 복제를 중지하거나 수동 재스냅샷 없이 지원되는 소스 테이블 스키마 변경(DDL)을 적용해요. 이를 위해 커넥터는 SQL Server 캡처 인스턴스를 자율적으로 관리해요. 추적 중인 테이블의 스키마가 변경되면 커넥터는 업데이트된 스키마용 새 캡처 인스턴스를 만들고, 전환이 끝난 뒤 이전 인스턴스를 drop해요. 프로세스 개요는 스키마 변경을 참고해요.

캡처 인스턴스를 만들고 drop하는 것은 일반적으로 db_owner가 필요해요. 커넥터에 그 수준의 접근을 부여하는 대신, 커넥터를 대신해 이러한 작업을 수행하는 작은 래퍼 프로시저 세트를 배포하고 커넥터에게 그 두 프로시저만 실행할 권한을 부여해요.

이 설계는 다음과 같은 속성을 가져요.

  • 커넥터는 두 래퍼 프로시저만 실행할 수 있어요. 캡처 인스턴스 관리를 위해 커넥터는 dbo.sf_openflow_cdc_enable_table과 dbo.sf_openflow_cdc_disable_table에만 EXECUTE가 부여돼요. db_owner를 보유하지 않으며 기본 sys.sp_cdc_enable_table이나 sys.sp_cdc_disable_table 프로시저를 직접 호출할 수 없어요. 래퍼 프로시저는 EXECUTE AS OWNER로 실행되므로 특정 감사된 작업에 대해서만 상승된 권한을 제공해요.
  • 모든 작업이 감사 테이블에 기록돼요. 래퍼 프로시저의 각 호출은 엔진을 호출하기 전에 dbo.openflow_cdc_audit 테이블에 attempt 행을 쓰고, 호출 후에는 success 또는 failure 행(실패 시 SQL Server 오류 번호와 메시지 포함)을 써요. 행은 append-only이며, 래퍼는 감사 행을 업데이트하거나 삭제하지 않아요.

데이터베이스 관리자로 프로시저를 배포해요. 복제 중인 각 CDC 활성 데이터베이스에서 다음 스크립트를 순서대로 실행해요. 이미 db_owner를 보유한 보안 주체로 실행해요.

참고

SQL Server 인스턴스 설정에서 설명한 대로 커넥터의 데이터베이스 사용자를 만든 뒤 데이터베이스 관리자로 이 작업을 수행해요.

  • openflow_cdc_audit_setup.sql: 래퍼 프로시저가 기록하는 append-only dbo.openflow_cdc_audit 테이블을 만들어요.

    SET NOCOUNT ON;
    
    -- Audit log. Each wrapper invocation writes one 'attempt' row before delegating to
    -- sys.sp_cdc_*, then either a 'success' or 'failure' row sharing the same attempt_id.
    -- Rows are append-only.
    IF OBJECT_ID(N'dbo.openflow_cdc_audit', N'U') IS NULL
    BEGIN
        CREATE TABLE dbo.openflow_cdc_audit (
            audit_id         bigint           IDENTITY(1,1) NOT NULL CONSTRAINT pk_openflow_cdc_audit PRIMARY KEY,
            attempt_id       uniqueidentifier NOT NULL,
            event_time       datetime2(3)     NOT NULL,
            event_kind       varchar(16)      NOT NULL,
            action           varchar(16)      NOT NULL,
            source_schema    sysname          NOT NULL,
            source_name      sysname          NOT NULL,
            capture_instance sysname          NOT NULL,
            caller           sysname          NOT NULL,
            error_number     int              NULL,
            error_message    nvarchar(4000)   NULL,
            CONSTRAINT ck_openflow_cdc_audit_event_kind CHECK (event_kind IN ('attempt', 'success', 'failure')),
            CONSTRAINT ck_openflow_cdc_audit_action     CHECK (action     IN ('enable',  'disable'))
        );
    
        CREATE INDEX ix_openflow_cdc_audit_attempt    ON dbo.openflow_cdc_audit(attempt_id);
        CREATE INDEX ix_openflow_cdc_audit_event_time ON dbo.openflow_cdc_audit(event_time);
    END;
    GO
    
  • sf_openflow_cdc_enable_table.sql: 스키마 전환 중 커넥터가 새 캡처 인스턴스를 추가하도록 호출하는 래퍼 프로시저를 만들어요.

    CREATE OR ALTER PROCEDURE dbo.sf_openflow_cdc_enable_table
        @source_schema    sysname,
        @source_name      sysname,
        @capture_instance sysname
    WITH EXECUTE AS OWNER
    AS
    BEGIN
        SET NOCOUNT ON;
    
        -- 1. Table must exist and not be a system table.
        IF OBJECT_ID(QUOTENAME(@source_schema) + N'.' + QUOTENAME(@source_name), N'U') IS NULL
            THROW 50001, 'Source table does not exist or is not a user table.', 1;
    
        -- 2. The connector supplies the full capture instance name. Validate that
        --    it is present and fits within the 100-character limit imposed by CDC.
        IF @capture_instance IS NULL OR LEN(@capture_instance) = 0
            THROW 50002, 'Capture instance name must be provided.', 1;
    
        IF LEN(@capture_instance) > 100
            THROW 50003, 'Capture instance name exceeds the 100-character limit.', 1;
    
        -- 3. Record the attempt.
        DECLARE @attempt_id uniqueidentifier = NEWID();
        DECLARE @caller     sysname          = ORIGINAL_LOGIN();
    
        INSERT INTO dbo.openflow_cdc_audit
            (attempt_id, event_time, event_kind, action, source_schema, source_name, capture_instance, caller)
        VALUES
            (@attempt_id, SYSUTCDATETIME(), 'attempt', 'enable',
             @source_schema, @source_name, @capture_instance, @caller);
    
        -- 4. Delegate to the engine procedure and record the outcome.
        BEGIN TRY
            EXEC sys.sp_cdc_enable_table
                @source_schema    = @source_schema,
                @source_name      = @source_name,
                @capture_instance = @capture_instance,
                @role_name        = NULL;
    
            INSERT INTO dbo.openflow_cdc_audit
                (attempt_id, event_time, event_kind, action, source_schema, source_name, capture_instance, caller)
            VALUES
                (@attempt_id, SYSUTCDATETIME(), 'success', 'enable',
                 @source_schema, @source_name, @capture_instance, @caller);
        END TRY
        BEGIN CATCH
            DECLARE @err_num int            = ERROR_NUMBER();
            DECLARE @err_msg nvarchar(4000) = ERROR_MESSAGE();
    
            INSERT INTO dbo.openflow_cdc_audit
                (attempt_id, event_time, event_kind, action, source_schema, source_name,
                 capture_instance, caller, error_number, error_message)
            VALUES
                (@attempt_id, SYSUTCDATETIME(), 'failure', 'enable',
                 @source_schema, @source_name, @capture_instance, @caller,
                 @err_num, @err_msg);
    
            ;THROW;
        END CATCH
    END;
    GO
    
  • sf_openflow_cdc_disable_table.sql: 스키마 전환이 끝난 뒤 커넥터가 이전 캡처 인스턴스를 drop하도록 호출하는 래퍼 프로시저를 만들어요.

    CREATE OR ALTER PROCEDURE dbo.sf_openflow_cdc_disable_table
        @source_schema    sysname,
        @source_name      sysname,
        @capture_instance sysname
    WITH EXECUTE AS OWNER
    AS
    BEGIN
        SET NOCOUNT ON;
    
        -- 1. The connector supplies the full capture instance name. Validate that it is present.
        IF @capture_instance IS NULL OR LEN(@capture_instance) = 0
            THROW 50002, 'Capture instance name must be provided.', 1;
    
        -- 2. Record the attempt.
        DECLARE @attempt_id uniqueidentifier = NEWID();
        DECLARE @caller     sysname          = ORIGINAL_LOGIN();
    
        INSERT INTO dbo.openflow_cdc_audit
            (attempt_id, event_time, event_kind, action, source_schema, source_name, capture_instance, caller)
        VALUES
            (@attempt_id, SYSUTCDATETIME(), 'attempt', 'disable',
             @source_schema, @source_name, @capture_instance, @caller);
    
        -- 3. Delegate to the engine procedure and record the outcome.
        BEGIN TRY
            EXEC sys.sp_cdc_disable_table
                @source_schema    = @source_schema,
                @source_name      = @source_name,
                @capture_instance = @capture_instance;
    
            INSERT INTO dbo.openflow_cdc_audit
                (attempt_id, event_time, event_kind, action, source_schema, source_name, capture_instance, caller)
            VALUES
                (@attempt_id, SYSUTCDATETIME(), 'success', 'disable',
                 @source_schema, @source_name, @capture_instance, @caller);
        END TRY
        BEGIN CATCH
            DECLARE @err_num int            = ERROR_NUMBER();
            DECLARE @err_msg nvarchar(4000) = ERROR_MESSAGE();
    
            INSERT INTO dbo.openflow_cdc_audit
                (attempt_id, event_time, event_kind, action, source_schema, source_name,
                 capture_instance, caller, error_number, error_message)
            VALUES
                (@attempt_id, SYSUTCDATETIME(), 'failure', 'disable',
                 @source_schema, @source_name, @capture_instance, @caller,
                 @err_num, @err_msg);
    
            ;THROW;
        END CATCH
    END;
    GO
    
  • openflow_cdc_grants.sql: 커넥터의 데이터베이스 사용자에게 두 래퍼 프로시저 실행 권한과 CDC 메타데이터, 변경 테이블, 감사 경로를 읽을 권한을 부여해요. <user_name>을 SQL Server 인스턴스 설정에서 만든 커넥터의 데이터베이스 사용자로 바꾼 뒤 스크립트를 실행해요.

    SET NOCOUNT ON;
    
    -- Replace <user_name> with the connector's SQL Server database user.
    DECLARE @connector sysname = N'<user_name>';
    
    -- The principal must already exist in this database.
    IF DATABASE_PRINCIPAL_ID(@connector) IS NULL
        THROW 50100,
              'Connector principal does not exist in this database. Create the user first, then re-run this script.',
              1;
    
    DECLARE @sql nvarchar(max);
    
    -- 1. Allow the connector to call the two wrapper procedures. All CDC management
    --    goes through these wrappers, so no elevated role is required.
    SET @sql = N'GRANT EXECUTE ON dbo.sf_openflow_cdc_enable_table  TO ' + QUOTENAME(@connector);
    EXEC sys.sp_executesql @sql;
    
    SET @sql = N'GRANT EXECUTE ON dbo.sf_openflow_cdc_disable_table TO ' + QUOTENAME(@connector);
    EXEC sys.sp_executesql @sql;
    
    -- 2. Allow the connector to read its own audit trail.
    SET @sql = N'GRANT SELECT ON dbo.openflow_cdc_audit TO ' + QUOTENAME(@connector);
    EXEC sys.sp_executesql @sql;
    
    -- 3. Allow the connector to enumerate capture instances and read CDC change tables.
    SET @sql = N'GRANT SELECT ON SCHEMA::cdc TO ' + QUOTENAME(@connector);
    EXEC sys.sp_executesql @sql;
    GO
    

    참고

    커넥터는 각 복제 소스 테이블에 대한 SELECT도 필요해요. SQL Server는 db_owner를 보유하지 않은 호출자에게 cdc.change_tables에 대한 행 수준 필터링을 적용해 호출자가 읽을 수 있는 소스 테이블의 행만 반환해요. SQL Server 인스턴스 설정에서 부여한 db_datareader 역할이 이 요구 사항을 충족해요. db_datareader 대신 접근을 더 좁게 범위를 지정했다면 커넥터가 복제하는 모든 소스 테이블에 SELECT 권한이 있는지 확인해요.

  • 배포를 검증해요. 두 래퍼 프로시저가 존재하고 감사 테이블을 쿼리할 수 있는지 확인해요.

    SELECT name FROM sys.procedures WHERE name LIKE 'sf_openflow%';
    SELECT TOP 1 1 FROM dbo.openflow_cdc_audit;
    

    첫 번째 쿼리는 sf_openflow_cdc_enable_table과 sf_openflow_cdc_disable_table 둘 다를 반환해요. 두 번째 쿼리는 감사 테이블이 존재하고 읽을 수 있는지 확인해요.

Snowflake 환경 설정 (Set up your Snowflake environment)

Openflow 관리자로서 이 커넥터에 대해 다음 작업을 수행해요. 기본 SNOWFLAKE_MANAGED 인증 전략에서는 런타임의 execute-as 역할이 커넥터가 Snowflake에 접근할 때 사용하는 정체성이므로, 이 역할에 권한을 부여해요.

참고

Openflow - BYOC 배포에 커넥터를 배포하고 권장되는 SNOWFLAKE_MANAGED 대신 KEY_PAIR 인증 전략을 사용한다면, 런타임의 관리 토큰에 의존하지 않고 이 동일한 execute-as 역할을 서비스 사용자에게 부여해야 해요. 서비스 사용자를 만들려면 Openflow - BYOC 배포용 키-페어 인증 설정을 참고해요.

  • 복제된 데이터를 저장할 데이터베이스를 만들고 execute-as 역할에 USAGE와 CREATE SCHEMA를 부여해요. 커넥터는 대상 스키마를 자동으로 만들어요. Snowflake는 다른 커넥터를 포함한 다른 데이터 소스와의 충돌을 피하기 위해 커넥터당 전용 대상 데이터베이스를 권장해요. 이 대상 데이터베이스를 런타임, 커넥터, 비밀 같은 Openflow 인프라스트럭처 객체를 보유한 데이터베이스와 분리해 두세요. 커넥터는 소스 스키마와 테이블 이름을 기반으로 대상 객체를 만들므로 그 이름은 사용자가 통제하지 못하며 소스가 변경되면 바뀔 수 있어요.

    CREATE DATABASE IF NOT EXISTS <destination_database>;
    
    GRANT USAGE ON DATABASE <destination_database> TO ROLE OPENFLOW_<RUNTIME_NAME>_EXECUTE_AS_RL;
    GRANT CREATE SCHEMA ON DATABASE <destination_database> TO ROLE OPENFLOW_<RUNTIME_NAME>_EXECUTE_AS_RL;
    
  • 커넥터가 사용할 웨어하우스를 지정하고 execute-as 역할에 USAGE와 OPERATE를 부여해요. XSMALL 웨어하우스 크기로 시작한 다음, 복제되는 테이블 수와 전송되는 데이터 양에 따라 크기를 실험해요. 테이블 수가 많으면 웨어하우스 크기보다는 멀티 클러스터 웨어하우스가 더 잘 확장되는 경향이 있어요.

    CREATE WAREHOUSE <ingest_warehouse>
      WITH
        WAREHOUSE_SIZE = 'XSMALL'
        AUTO_SUSPEND = 300
        AUTO_RESUME = TRUE;
    
    GRANT USAGE, OPERATE ON WAREHOUSE <ingest_warehouse> TO ROLE OPENFLOW_<RUNTIME_NAME>_EXECUTE_AS_RL;
    
  • Snowflake 배포에만 해당: 이 커넥터의 소스 호스트와 포트가 런타임의 외부 접근 통합(external access integration, EAI)이 허용하는 네트워크 규칙에 의해 허용되는지 확인해요. EAI 자체는 이 커넥터가 아니라 런타임에 속해요. 한 번 만들어 런타임에 연결하고 execute-as 역할에 USAGE를 부여해요. 해당 단계는 네트워크 규칙 및 외부 접근 통합 만들기를 참고해요. 이 커넥터에 특정한 것은 소스 호스트를 EAI가 참조하는 규칙에 넣는 것이에요. 규칙은 소스의 호스트와 포트를 db.example.com:<port> 같은 단일 값으로 사용해요. 이는 커넥터 연결 URL의 jdbc: 스킴, 드라이버 이름, 데이터베이스 경로를 제외한 호스트와 포트예요. BYOC 배포는 클라우드 환경에서 아웃바운드 연결을 처리하며 EAI나 네트워크 규칙을 사용하지 않아요.

커넥터 설치 (Install the connector)

데이터 엔지니어로서 커넥터를 설치하려면 다음을 수행해요.

  1. Openflow의 Connector library 탭으로 이동해요.

  2. Openflow 커넥터 페이지에서 커넥터를 찾고 Install을 선택해요.

  3. Select runtime 대화 상자에서 Available runtimes 드롭다운 목록에서 런타임을 선택하고 Install을 클릭해요.

    참고

    커넥터를 설치하기 전에 수집된 데이터를 저장할 데이터베이스와 스키마를 Snowflake에 만들었는지 확인해요.

  4. Snowflake 계정 자격 증명으로 배포에 인증하고, 런타임 애플리케이션이 Snowflake 계정에 접근하도록 허용할지 묻는 메시지가 표시되면 Allow를 선택해요. 커넥터 설치 과정은 완료까지 몇 분 걸려요.

  5. Snowflake 계정 자격 증명으로 런타임에 인증해요.

Openflow 캔버스가 나타나고 커넥터 프로세스 그룹이 추가돼요.

런타임 크기 조정 (Runtime sizing)

노드 유형 계층과 생성 후 크기 조정 방법을 포함한 크기 조정 안내는 CDC 커넥터용 런타임 크기 조정 및 패킹을 참고해요. 마이그레이션 지침은 커넥터 재설치를 참고해요.

커넥터 구성 (Configure the connector)

데이터 엔지니어로서 커넥터를 구성하려면 다음을 수행해요.

  1. 가져온 프로세스 그룹을 마우스 오른쪽 버튼으로 클릭하고 Parameters를 선택해요.
  2. 필요한 매개변수 값을 채워요. 필수 매개변수 값에 대한 자세한 내용은 다음 섹션을 참고해요.
    • SQLServer Source Parameters: SQL Server와 연결을 수립하는 데 사용돼요.
    • SQLServer Destination Parameters: Snowflake와 연결을 수립하는 데 사용돼요.
    • SQLServer Ingestion Parameters: 복제할 테이블을 지정하는 데 사용돼요.

먼저 SQLServer Source Parameters 컨텍스트의 매개변수를 설정한 다음 SQLServer Destination Parameters 컨텍스트를 설정해요. 완료한 뒤 커넥터를 활성화해요. 커넥터는 SQL Server와 Snowflake 둘 다에 연결해 실행을 시작해요. 그러나 복제할 테이블이 명시적으로 구성을 추가될 때까지 커넥터는 어떤 데이터도 복제하지 않아요.

특정 테이블을 복제하도록 구성하려면 SQLServer Ingestion Parameters 컨텍스트를 편집해요. SQLServer Ingestion Parameters 컨텍스트에 변경 사항을 적용하면 커넥터가 구성을 사용하고 모든 테이블에 대해 복제 수명 주기가 시작돼요.

한 런타임에서 여러 CDC 커넥터 인스턴스를 실행하려면 를 참고해요.

참고

DBCPConnectionPool 검증

DBCPConnectionPool 컨트롤러 서비스를 활성화하면 연결 풀을 구성하지만 JDBC 연결을 열지는 않아요.

Openflow 런타임에서 연결을 테스트하려면 컨트롤러 서비스 구성을 열고 Verify를 선택해요. Establish Connection 단계가 성공하는지 확인해요. 이 테스트는 구성된 JDBC URL, 드라이버, 사용자 이름, 암호를 사용해 연결을 열어요.

SQLServer Source Parameters

매개변수 설명
SQLServer Connection URL 소스에 연결하는 데 사용되는 전체 JDBC URL. 독립 실행형 SQL Server 인스턴스 또는 Azure SQL Managed Instance의 경우 URL을 인스턴스에 지정해요. 커넥터는 해당 인스턴스에서 복제할 데이터베이스를 발견해요. jdbc:sqlserver://example.com:1433;encrypt=false. Always On Availability Groups의 경우 Always On Availability Groups를 참고해요. Azure SQL Database의 경우 databaseName 속성을 사용해 URL을 특정 데이터베이스에 지정해요. 복제하려는 데이터베이스당 커넥터 인스턴스를 하나 사용해요. jdbc:sqlserver://your-server.database.windows.net:1433;encrypt=true;databaseName=your_database
SQLServer JDBC Driver Reference asset 확인란을 선택해 SQL Server JDBC 드라이버를 업로드해요.
SQLServer Username 커넥터의 사용자 이름.
SQLServer Password 커넥터의 암호.
SQLServer Query Interval 테이블 변경에 대한 다음 쿼리를 예약하기 전에 경과해야 하는 최소 시간 간격. 증분 복제 중 과도한 쿼리를 방지하기 위한 데이터베이스 폴링 빈도를 제어해요. 기본값: 10초.

참고

NTLMv2를 사용한 Windows 인증으로 연결하려면 SQL Server 소스 매개변수를 다음과 같이 구성해요.

  • SQLServer Connection URL: jdbc:sqlserver://<host>:1433;databaseName=<db>;integratedSecurity=true;authenticationScheme=NTLM;domain=<domain>;
  • SQLServer JDBC Driver: mssql-jdbc JAR을 업로드해요. 드라이버 클래스 이름은 com.microsoft.sqlserver.jdbc.SQLServerDriver예요.
  • SQLServer Username: 도메인 사용자를 입력해요.
  • SQLServer Password: 도메인 암호를 입력해요.

참고

Azure SQL Database는 Azure SQL Managed Instance가 아닌 단일 데이터베이스 PaaS 서비스를 가리켜요.

참고

프록시를 통한 Azure SQL Managed Instance

프록시, Nginx 라우트 또는 다른 중간 호스트를 통해 Azure SQL Managed Instance에 연결한다면 Azure SQL Managed Instance를 Proxy 연결 모드로 구성해요.

Openflow 런타임이 SQL Server 트래픽을 프록시 호스트를 통해 라우팅해야 할 때 Redirect 모드를 사용하지 마세요. Redirect 모드에서 Azure SQL Managed Instance는 JDBC 드라이버에게 다른 백엔드 호스트로 다시 연결하도록 지시할 수 있어요. 그 리다이렉트된 호스트는 Openflow 런타임에 구성된 프록시 라우트를 통해 도달하지 못할 수 있어요.

예를 들어:

  • Openflow가 nginx.example.com:10001에 연결해요.
  • Nginx가 트래픽을 sqlmi.example.database.windows.net:1433으로 전달해요.
  • Redirect 모드의 Azure SQL Managed Instance가 JDBC 드라이버에게 다른 백엔드 호스트로 다시 연결하도록 지시해요.
  • Openflow 런타임이 그 리다이렉트된 호스트에 직접 연결하려 해요.
  • 리다이렉트된 호스트가 프록시를 통해 라우팅되지 않으므로 연결이 실패해요.

모든 SQL Server 트래픽이 원래 프록시 라우트에 유지되어야 한다면 Proxy 모드를 사용해요.

SQLServer Username에는 Azure SQL Managed Instance에 구성된 SQL 로그인을 입력해요.

Always On Availability Groups

커넥터가 개별 복제본 노드가 아니라 가용성 그룹 리스너(그룹의 가상 네트워크 이름)를 통해 연결하도록 구성해요. SQLServer Source Parameters 컨텍스트의 SQLServer Connection URL 매개변수에 리스너 호스트 이름을 설정해요.

Always On Availability Groups는 공유 리스너와 복제본 간 자동 장애 조치를 통해 고가용성을 제공해요. Always On Availability Groups는 SQL Server 트랜잭션 복제와는 별개예요.

경고

복제가 시작된 뒤 연결 대상을 변경하지 마세요. 각 데이터베이스는 자체 복제 위치를 독립적으로 유지하므로, 다른 서버나 리스너로 전환하면 커넥터가 이미 처리된 변경 사항을 추적하지 못할 수 있어요. 이는 데이터 손실을 초래할 수 있어요.

고객 관리형 Azure Private Link Service(PLS)를 통해 SQL Server에 연결한다면 SYSTEM$GET_PRIVATELINK_ENDPOINTS_INFO()가 반환하는 호스트 값을 SQLServer Connection URL의 호스트 이름으로 사용해요. 이는 Snowflake 아웃바운드 개인 연결 엔드포인트가 프로비저닝될 때 제공된 논리적 호스트 이름이에요. PLS 별칭이나 리소스 ID는 프로비저닝 중 Azure 서비스를 식별하지만 JDBC 호스트 이름은 아니에요.

예를 들어:

  • jdbc:sqlserver://<host>:1433;databaseName=<db>

같은 호스트 이름과 포트를 Openflow egress 네트워크 규칙에 추가해요.

TYPE = PRIVATE_HOST_PORT
MODE = EGRESS
VALUE_LIST = ('<host>:1433')

:1433 포트를 명시적으로 포함해요. PRIVATE_HOST_PORT 네트워크 규칙에서 포트를 지정하지 않으면 기본값 443으로 설정되는데, 이는 SQL Server 리스너와 일치하지 않아요.

Snowflake는 이 등록된 호스트 이름을 아웃바운드 개인 엔드포인트를 통해 라우팅해요. Azure에서는 백엔드 라우트, 상태 프로브, SQL Server 리스너, 포트 전달이 같은 포트로 트래픽을 전달하도록 PLS와 그 표준 Load Balancer 또는 Direct Connect 대상을 구성해요.

이 지침은 SQL Server 앞에 있는 고객 관리형 Azure PLS에 적용돼요. 네이티브 Azure SQL Managed Instance 개인 엔드포인트에는 적용되지 않아요.

엔드포인트를 프로비저닝하고 승인하려면(SYSTEM$PROVISION_PRIVATELINK_ENDPOINT 및 Azure 쪽 승인) Microsoft Azure의 외부 네트워크 접근 및 개인 연결과 아웃바운드 네트워크 트래픽용 개인 연결을 참고해요.

Always On Availability Groups의 JDBC URL에 ApplicationIntent를 사용하는 경우:

참고

읽기 가능한 보조(secondary)로 읽기를 라우팅하려면 가용성 그룹 리스너에서 읽기 전용 라우팅이 구성되었을 때 SQLServer Connection URL에 ;ApplicationIntent=ReadOnly를 추가해요.

읽기 가능한 보조를 노출하지 않는 토폴로지(예: 단일 엔드포인트가 있는 AWS RDS Multi-AZ)에서는 ApplicationIntent=ReadOnly가 설정되어 있어도 드라이버가 주(primary)에 연결해요.

연결이 읽기 가능한 보조로 라우팅되면 커넥터는 해당 복제본의 CDC 변경 테이블에서 읽어요. 이 테이블들은 주에서 캡처 지연과 redo 지연 이후에만 변경 사항을 반영하므로, 복제 지연이 주에 연결할 때보다 높을 수 있어요. 지연을 줄이려면 주에서 SQL Server CDC 캡처와 가용성 그룹 redo 설정을 조정해요.

가용성 그룹 장애 조치 중 복제는 자동으로 재개되고 테이블은 FAILED로 이동하지 않아요. 각 장애 조치 창 동안 커넥터는 데이터 이동이 일시 중지되거나 복제본이 읽기 접근용으로 활성화되지 않아 데이터베이스에 쿼리할 수 없다는 일시적 오류를 기록해요. 이 오류는 전환 중에 예상되는 것이며, 커넥터는 장애 조치가 완료되면 재시도하고 복구해요.

읽기 전용 라우팅 없이 리스너를 통해 주에 연결하려면 ApplicationIntent를 생략하거나 기본 ReadWrite 의도를 사용해요.

장애 조치 동작은 Always On Availability Groups 및 소스 장애 조치를 참고해요.

SQLServer Destination Parameters

매개변수 설명 필수
Destination Database 데이터가 저장되는 데이터베이스. Snowflake에 이미 존재해야 해요. 이름은 대소문자를 구분해요. 따옴표 없는 식별자는 대문자로 제공해요. 예
Destination Schema Pattern 데이터가 저장되는 대상 스키마 이름의 패턴. 스키마가 없으면 커넥터가 만들어요. 다음 선택적 변수를 사용해 수집되는 테이블별로 패턴을 사용자 지정할 수 있어요. ${source.database.name}: 소스 테이블의 데이터베이스. ${source.schema.name}: 소스 테이블의 스키마. ${source.table.name}: 소스 테이블의 이름. 예를 들어 정규화 이름이 source_db.tenant_a.data인 테이블에서 패턴 prefix_${source.database.name}_${source.schema.name}은 prefix_source_db_tenant_a로 계산돼요. 모든 테이블을 단일 스키마로 수집하려면 변수 없는 스키마 이름(예: destination_schema)을 제공해요. 중요: 커넥터가 데이터를 수집하기 시작한 뒤에는 이 설정을 변경하지 마세요. 수집 시작 후 이 설정을 변경하면 기존 수집이 깨져요. 이 설정을 변경해야 한다면 새 커넥터 인스턴스를 만들어요. 예
Snowflake Authentication Strategy 사용할 때: Snowflake Openflow Deployment 또는 BYOC: SNOWFLAKE_MANAGED 사용. 이 토큰은 Snowflake가 자동으로 관리해요. BYOC 배포는 SNOWFLAKE_MANAGED를 쓰려면 먼저 execute-as 역할을 구성해야 해요. BYOC: 선택적으로 BYOC는 인증 전략 값으로 KEY_PAIR를 사용할 수 있어요. 예
Snowflake Account Identifier 사용할 때: SNOWFLAKE_MANAGED 인증 전략 → 비어 있어야 해요. KEY_PAIR → [organization-name]-[account-name] 형식의 Snowflake 계정 이름. 예
Snowflake Connection Strategy KEY_PAIR를 사용할 때 Snowflake에 연결하는 전략을 지정해요: STANDARD(기본값): 표준 공용 라우팅으로 Snowflake 서비스에 연결해요. PRIVATE_CONNECTIVITY: AWS PrivateLink 같은 지원 클라우드 플랫폼과 연관된 개인 주소로 연결해요. KEY_PAIR가 있는 BYOC에만 필수, 그 외에는 무시.
Snowflake Object Identifier Resolution 소스 객체 식별자(예: 스키마, 테이블, 열 이름)가 Snowflake에서 저장되고 쿼리되는 방식을 지정해요. SQL 쿼리에서 큰따옴표를 사용해야 하는지 여부를 결정해요. 옵션 1: 기본값, 대소문자 구분 없음(권장): 변환: 모든 식별자가 대문자로 변환돼요. 예: My_Table → MY_TABLE. 쿼리: SQL 쿼리는 대소문자를 구분하지 않으며 SQL 큰따옴표가 필요하지 않아요. 예: SELECT * FROM my_table;은 SELECT * FROM MY_TABLE;과 같은 결과를 반환해요. 참고: 데이터베이스 객체에 혼합 대소문자 이름이 예상되지 않는다면 이 옵션을 권장해요. 중요: 커넥터 수집이 시작된 뒤에는 이 설정을 변경하지 마세요. 수집 시작 후 이 설정을 변경하면 기존 수집이 깨져요. 변경해야 한다면 새 커넥터 인스턴스를 만들어요. 옵션 2: 대소문자 구분: 변환: 대소문자가 보존돼요. 예: My_Table은 My_Table로 유지돼요. 쿼리: SQL 쿼리는 데이터베이스 객체의 정확한 대소문자를 일치시키기 위해 큰따옴표를 사용해야 해요. 예: SELECT * FROM "My_Table";. 참고: 레거시 또는 호환성 이유로 소스 대소문자를 보존해야 한다면 이 옵션을 권장해요. 예를 들어 소스 데이터베이스가 MY_TABLE과 my_table처럼 대소문자만 다른 테이블 이름을 포함하면, 대소문자 구분 없는 비교에서 이름 충돌이 발생해요. 예
Snowflake Private Key 사용할 때: SNOWFLAKE_MANAGED 인증 전략 → 비어 있어야 해요. KEY_PAIR → 인증에 사용되는 RSA 개인 키로, PKCS8 표준에 따라 형식화되고 표준 PEM 헤더/푸터를 포함해야 해요. Snowflake Private Key File 또는 Snowflake Private Key 중 하나는 반드시 정의해야 해요. 아니요
Snowflake Private Key File 사용할 때: SNOWFLAKE_MANAGED 인증 전략 → 개인 키 파일은 비어 있어야 해요. KEY_PAIR → 인증에 사용되는 RSA 개인 키가 포함된 파일을 업로드해요. PKCS8 표준에 따라 형식화되고 표준 PEM 헤더/푸터를 포함해야 해요. 헤더 줄은 -----BEGIN PRIVATE로 시작해요. 개인 키 파일을 업로드하려면 Reference asset 확인란을 선택해요. 아니요
Snowflake Private Key Password 사용할 때: SNOWFLAKE_MANAGED 인증 전략 → 비어 있어야 해요. KEY_PAIR → Snowflake Private Key File과 연결된 암호를 제공해요. 아니요
Snowflake Role 사용할 때: SNOWFLAKE_MANAGED 인증 전략 → 런타임의 execute-as 역할(또는 그 역할에 부여된 하위 역할)을 사용해요. Openflow UI에서 런타임의 View Details로 이동해 execute-as 역할을 찾을 수 있어요. KEY_PAIR → 서비스 사용자에게 구성된 유효한 역할을 사용해요. 예
Snowflake Username 사용할 때: SNOWFLAKE_MANAGED 인증 전략 → 비어 있어야 해요. KEY_PAIR → Snowflake 인스턴스에 연결하는 데 사용되는 사용자 이름을 제공해요. 예
Oversized Value Strategy 복제 중 내부 크기 한도(16 MB)를 초과하는 값을 커넥터가 처리하는 방식을 결정해요. 가능한 값: Fail Table(기본값): 테이블이 영구 실패로 표시되고 해당 테이블의 복제가 중지돼요. Set Null: 값이 대상 테이블에서 NULL로 대체돼요. 대용량 값을 초과하는 테이블에서 데이터 손실을 허용할 수 있을 때 테이블 실패를 방지하려면 이를 사용해요. 아니요
Table Storage Format 표준 Snowflake 테이블 또는 Iceberg 테이블. 기본값은 STANDARD. 커넥터 시작 후에는 변경하지 마세요. 예
Iceberg Version Iceberg 테이블 버전, 2 또는 3(기본값 3). Table Storage Format이 ICEBERG인 경우에만 적용돼요. 수집 시작 후에는 이 값을 변경하지 마세요. 아니요
Snowflake Warehouse 쿼리를 실행하는 데 사용되는 Snowflake 웨어하우스. 예

SQLServer Ingestion Parameters

매개변수 설명
Column Filter JSON 선택 사항. 테이블별로 포함/제외할 열을 지정하는 필터 객체의 JSON 배열. 문법 세부 사항과 예시는 테이블의 열 하위 집합 복제를 참고해요.
Concurrent Select Queries For Incremental 증분 복제 중 소스 데이터베이스에 대해 실행할 동시 SELECT 쿼리의 최대 수. 기본값: 1, 최대값: 8. 증가시키면 많은 테이블이 활성 상태일 때 복제 속도를 높일 수 있지만 소스 데이터베이스의 부하도 증가해요.
Concurrent Select Queries For Snapshot Snapshot 플로우에서 소스 데이터베이스에 대해 실행할 동시 쿼리의 최대 수. 증가시키면 많은 수의 테이블 스냅샷 속도를 높일 수 있지만 소스 데이터베이스의 부하도 증가해요.
Included Table Names 소스 테이블 경로의 쉼표로 구분된 목록(데이터베이스와 스키마 포함). 예: database_1.public.table_1, database_2.schema_2.table_2
Included Table Regex 데이터베이스와 스키마 이름을 포함해 테이블 경로와 일치시키는 정규 표현식. 표현식과 일치하는 모든 경로가 복제되고, 나중에 만든 일치하는 새 테이블도 자동으로 포함돼요. 예: database_name\\.public\\.auto_.*
Ingestion Type 새로 추가된 테이블이 증분 CDC 복제로 전환되기 전에 전체 초기 스냅샷을 거칠지, 아니면 스냅샷을 건너뛰고 증분 CDC 복제만 시작할지를 제어해요. full(기본값)로 설정하면 스냅샷 후 증분 복제. incremental로 설정하면 새로 추가된 테이블의 스냅샷을 건너뛰고 이후 변경만 복제해요. 이 값을 변경해도 이미 복제를 시작한 테이블에는 영향을 주지 않아요. 사용 메모는 스냅샷 없는 증분 복제 설정을 참고해요.
Merge Task Schedule CRON Journal에서 Destination Table로의 병합 작업이 트리거되는 기간을 정의하는 CRON 표현식. 지속적인 병합을 원하면 * * * * * ?로 설정하고, 웨어하우스 실행 시간을 제한하려면 시간 일정을 구성해요. 커넥터는 이 일정을 UTC 표준시로 평가해요. 예: 문자열 * 0 * * * ?은 정각에 1분 동안 병합을 예약하고 싶다는 뜻이에요. 문자열 * 20 14 ? * MON-FRI는 매주 월요일부터 금요일 오후 2시 20분에 병합을 예약하고 싶다는 뜻이에요. 추가 정보와 예시는 Quartz 문서의 cron 트리거 튜토리얼을 참고해요.
Re-read Tables in State Starting CDC Position이 Earliest일 때만 적용돼요. New(기본값): 시작 위치가 Earliest로 전환된 뒤 추가된 새 테이블만 가장 이른 위치부터 CDC 변경 테이블을 읽어요. 구성 변경 전에 복제를 시작한 테이블은 마지막 위치부터 계속 읽어요. Any active: 현재 복제 중인 모든 테이블의 변경 사항을 다시 읽고 다시 처리해요. 자세한 내용은 CDC 위치에서 로드 지정을 참고해요.
Re-snapshot Table Exclusions 포함 기준과 일치하는 테이블 중에서 복제하지 않아야 하는 정규화된 테이블 이름의 쉼표로 구분된 목록. Included Table Names와 같은 형식과 인용 규칙을 사용해요. 예: database_1.public.table_1.
SQL Server Read Timeout 스냅샷과 증분 쿼리 둘 다에 적용되는 읽기 타임아웃(밀리초). 이 값보다 오래 실행되는 쿼리는 SQL Server가 종료해요. 기본값: 60000.
Starting CDC Position Latest(기본값): CDC 변경 테이블 읽기가 사용 가능한 최신 위치에서 시작해 거기서부터 계속돼요. Earliest: 증분 로드가 사용 가능한 가장 이른 CDC 변경 테이블 위치부터 시작하거나 다시 읽도록 전환해요. 자세한 내용은 CDC 위치에서 로드 지정을 참고해요.
Table Key Configuration JSON 선택 사항. 하나 이상의 테이블에 대한 논리적 키를 선언하는 JSON 배열. 설정되면 논리적 키가 최우선 순위를 가지며 커넥터가 자동 감지하는 기본 키, 고유 제약 조건, 고유 인덱스를 모두 재정의해요. 커넥터는 이 매개변수를 MultiDatabaseJsonTableKeyConfigService 컨트롤러 서비스를 통해 읽어요. 문법 세부 사항과 예시는 테이블에 대한 논리적 키 지정을 참고해요.

SNAPSHOT 격리 하에서 소스 읽기 (Read the source under SNAPSHOT isolation)

스냅샷 단계 동안 커넥터는 초기 전체 복사를 수행하기 위해 소스 테이블에서 직접 읽어요. SQL Server의 기본 READ COMMITTED 격리 수준에서는 이러한 읽기가 공유 잠금을 획득하여 다른 데이터베이스 클라이언트의 동시 쓰기와 교착 상태(deadlock)가 될 수 있어요. 증분 복제 중 커넥터는 소스 테이블 대신 전용 CDC 변경 테이블에서 읽으므로 이러한 잠금을 취하지 않아요. 스냅샷 단계 중 교착 상태를 피하면서 다른 애플리케이션이 사용하는 격리 수준에는 영향을 주지 않으려면, 커넥터가 SNAPSHOT 격리 하에서 읽도록 구성해요. 배경은 소스 데이터베이스 잠금 동작을 참고해요.

두 단계로 커넥터에 SNAPSHOT 격리를 활성화해요.

  • 각 소스 데이터베이스에서 스냅샷 격리를 허용해요.

    ALTER DATABASE <database> SET ALLOW_SNAPSHOT_ISOLATION ON;
    
  • MultiDatabaseFetchTableSnapshot 프로세서에 Use Snapshot Isolation이라는 값이 true인 동적 속성을 추가해요. 스냅샷 단계만 소스 테이블에 공유 잠금을 취하므로 증분 복제에는 Use Snapshot Isolation 속성이 필요하지 않아요.

커넥터는 시작할 때 각 소스 데이터베이스를 확인하고 ALLOW_SNAPSHOT_ISOLATION이 활성화된 데이터베이스에 대해서만 SNAPSHOT 격리를 사용해요. 활성화되지 않은 데이터베이스의 경우 커넥터는 기본 격리 수준으로 폴백해요. 이 검사는 시작 시 실행되므로 ALLOW_SNAPSHOT_ISOLATION을 변경한 뒤에는 프로세서를 다시 시작해요.

주의

ALLOW_SNAPSHOT_ISOLATION은 SNAPSHOT 격리를 명시적으로 요청하는 세션(예: 커넥터)에서만 사용 가능하게 해요. 기본 READ COMMITTED 격리 수준을 변경하지 않으므로 소스 데이터베이스를 사용하는 다른 애플리케이션에는 영향이 없어요.

이 목적으로 READ_COMMITTED_SNAPSHOT(RCSI)을 사용하지 마세요. RCSI도 공유 잠금을 제거하지만 데이터베이스에 대한 모든 연결의 기본 READ COMMITTED 격리 수준을 재정의해요. 기본 잠금 기반 READ COMMITTED 동작에 의존하는 애플리케이션(예: 읽기가 동시 미커밋 쓰기를 차단할 것으로 기대하는 경우)은 변경 후 다른 결과를 볼 수 있어요.

테이블의 열 하위 집합 복제 (Replicate a subset of columns in a table)

커넥터는 테이블별로 복제되는 데이터를 구성된 열의 하위 집합으로 필터링할 수 있어요. 기본 키 열은 제외와 관계없이 항상 포함돼요.

열 필터를 적용하려면 Ingestion Parameters 컨텍스트의 Column Filter JSON 매개변수를 필터할 각 테이블당 하나씩, 필터 객체의 JSON 배열로 설정해요.

열은 이름이나 정규 표현식 패턴으로 포함/제외할 수 있어요. 테이블당 단일 조건을 적용하거나 여러 조건을 결합할 수 있으며, 제외가 항상 포함보다 우선해요.

문법 (Syntax)

배열의 각 객체는 테이블을 식별하고 포함/제외할 열을 지정해요. 이 커넥터는 3부분 정규화 이름(데이터베이스, 스키마, 테이블)을 사용하므로 각 객체는 schema와 table 필드에 더해 database 또는 databasePattern 필드를 포함할 수 있어요.

[
    {
        "database": "<database>" | "databasePattern": "<regex>",
        "schema": "<schema>" | "schemaPattern": "<regex>",
        "table": "<table>" | "tablePattern": "<regex>",
        "included": ["<column>", "<column>"],
        "excluded": ["<column>", "<column>"],
        "includedPattern": "<regex>",
        "excludedPattern": "<regex>"
    }
]

다음 규칙이 적용돼요.

  • 정확한 이름 일치에는 database, schema, table을, 정규 표현식 일치에는 databasePattern, schemaPattern, tablePattern을 사용해요. 같은 객체에서 필드와 그 패턴 변형을 둘 다 사용할 수 없어요(예: schema와 schemaPattern을 둘 다 넣을 수 없음).
  • included, excluded, includedPattern, excludedPattern 중 적어도 하나는 반드시 제공해야 해요.
  • 포함과 제외 필터를 둘 다 지정하면 제외가 우선해요.
  • 여러 필터가 같은 테이블과 일치하면 마지막 일치 필터가 사용되며, 정확한 일치가 패턴 기반 필터보다 우선해요.
  • 값은 다른 테이블에 다른 필터를 적용하는 객체 배열일 수 있어요.

예시 (Examples)

이름으로 특정 열 포함:

[
    {
        "database": "my_db",
        "schema": "dbo",
        "table": "orders",
        "included": ["account_id", "status", "created_at"]
    }
]

이름으로 특정 열 제외:

[
    {
        "database": "my_db",
        "schema": "dbo",
        "table": "orders",
        "excluded": ["internal_note", "debug_flag"]
    }
]

포함 패턴과 특정 제외 결합(예: admin_email을 제외한 모든 이메일 열 포함):

[
    {
        "database": "my_db",
        "schema": "dbo",
        "table": "contacts",
        "includedPattern": ".*_email",
        "excluded": ["admin_email"]
    }
]

데이터베이스 패턴과 정확한 스키마/테이블 이름을 섞어 여러 데이터베이스에 걸쳐 필터 적용:

[
    {
        "databasePattern": "prod_.*",
        "schema": "dbo",
        "table": "customers",
        "excluded": ["internal_note"]
    }
]

여러 필터 객체를 전달해 다른 테이블에 다른 규칙 적용:

[
    {"database": "my_db", "schema": "dbo", "table": "orders", "included": ["account_id", "status"]},
    {"database": "my_db", "schema": "dbo", "table": "customers", "excludedPattern": ".*_internal"}
]

같은 열 포함과 제외

테이블의 복제 세트에서 열을 제거(제외하거나 included 목록에서 제거)하면 대상에서 소스에서 열을 drop하는 것과 같은 효과가 있어요. 커넥터는 접미사(기본값 __SNOWFLAKE_DELETED)로 이름을 바꿔 대상의 열을 소프트 삭제해요. 이후 그 열을 복제 세트에 다시 추가하고 나중에 두 번째로 제거하면, 소프트 삭제된 열 이름이 이미 사용 중이므로 해당 테이블의 복제가 실패해요. 복구하려면 해당 테이블의 복제를 다시 시작해요.

파티션된 테이블 복제 (Replicate a partitioned table)

커넥터는 파티션된 테이블의 복제를 지원해요. SQL Server 파티션된 테이블은 모든 파티션의 데이터를 포함하는 단일 대상 테이블로 Snowflake에 복제돼요.

파티션된 테이블을 복제하려면 SQL Server 인스턴스 설정에서 설명한 대로 파티션된 테이블에서 CDC가 활성화되어 있는지 확인해요.

대규모 파티션된 테이블의 스냅샷을 커넥터가 처리하는 방법에 대한 자세한 내용은 파티션된 테이블의 스냅샷을 참고해요.

테이블에 대한 논리적 키 지정 (Specify a logical key for a table)

커넥터는 복제하는 모든 테이블에 복제 키가 필요해요. 기본적으로 커넥터는 테이블의 기본 키를 사용하고, 기본 키가 없으면 자격 있는 고유 제약 조건이나 고유 인덱스로 폴백해요. 커넥터가 키를 선택하는 데 사용하는 전체 우선순위 순서는 커넥터가 복제 키를 선택하는 방법을 참고해요. 논리적 키는 자동 감지된 키를 사용자가 선언한 대체물이에요. 다음과 같은 경우에 논리적 키를 구성해요.

  • 테이블에 기본 키가 없지만 하나 이상의 열이 데이터에서 고유한 경우
  • 커넥터가 자동 감지하는 것과 관계없이 특정 열 또는 열 집합을 복제 키로 사용해야 하는 경우(예: 합성 기본 키를 재정의하려는 경우)

논리적 키는 최우선 순위를 가져요. 커넥터가 테이블에 대한 논리적 키를 찾으면 그 키를 사용하고 테이블의 기본 키는 무시해요.

JSON 문법

Table Key Configuration JSON 값은 JSON 배열이에요. 각 항목은 하나의 테이블을 논리적 키 열에 매핑해요.

[
    {
        "database": "<database>",
        "schema": "<schema>",
        "table": "<table>",
        "logicalKey": ["<column>", "<column>"]
    }
]

필드는 다음과 같아요.

필드 설명
database 필수. 정확한 소스 데이터베이스 이름.
schema 필수. 정확한 소스 스키마 이름.
table 필수. 정확한 소스 테이블 이름.
logicalKey 필수. 테이블의 행을 고유하게 식별하는 소스 열 이름의 비어 있지 않은 배열.

다음 규칙이 적용돼요.

  • database, schema, table 일치는 대소문자를 구분해요. SQL Server가 보고하는 정확한 이름을 사용해요.
  • logicalKey 열 이름은 대소문자를 구분하지 않고 일치돼요. 커넥터는 비교 전에 구성된 이름과 소스 열 이름을 모두 소문자로 바꿔요. 대소문자 구분 SQL Server 데이터 정렬(collation)에서도 이 일치는 관대해요. 실제 열과 대소문자가 다른 키도 여전히 수용되며, 문자 대소문자만 다른 두 소스 열은 같은 키 열로 처리돼요. 모호함을 피하려면 정확한 열 대소문자를 사용해요.
  • database, schema, table이 어떤 복제 테이블과도 일치하지 않는 항목은 조용히 무시돼요.

논리적 키 구성 예시

기본 키가 없는 테이블의 단일 열 논리적 키:

[
    {
        "database": "SalesDB",
        "schema": "dbo",
        "table": "audit_log",
        "logicalKey": ["event_id"]
    }
]

복합 논리적 키:

[
    {
        "database": "SalesDB",
        "schema": "dbo",
        "table": "order_lines",
        "logicalKey": ["order_id", "line_item_id"]
    }
]

하나의 JSON 값에 여러 테이블의 논리적 키:

[
    {
        "database": "SalesDB",
        "schema": "dbo",
        "table": "audit_log",
        "logicalKey": ["event_id"]
    },
    {
        "database": "SalesDB",
        "schema": "dbo",
        "table": "order_lines",
        "logicalKey": ["order_id", "line_item_id"]
    }
]

제한 사항 (Restrictions)

다음 중 하나가 참이면 커넥터는 구성을 거부해요.

  • logicalKey가 누락되었거나, 비어 있거나, 배열이 아닌 경우
  • logicalKey에 중복 열 이름이 포함된 경우
  • logicalKey에 nullable 열이 포함된 경우. 논리적 키 열은 행을 안정적으로 식별하려면 NOT NULL로 정의되어야 해요.
  • logicalKey에 소스 테이블에 존재하지 않는 열 이름이 포함된 경우

구성이 거부되면 검증이 명확한 오류를 표시하고 테이블은 NEW 상태로 유지돼요(결코 FAILED가 아님). 구성을 수정한 뒤 상태를 재설정하지 않고 테이블의 복제가 재개돼요.

위험한 구성에 대한 경고 로그

커넥터는 다음 구성을 수용하지만 테이블 초기화 시 경고를 기록해요. 논리적 키 열을 선택할 때는 카디널리티가 높고, 가능하면 단조 증가하는 값을 가진 열을 선호해요. 카디널리티가 낮거나 비단조적인 키는 스냅샷 성능을 저하시킬 수 있어요.

  • 논리적 키 열이 부동 소수점 타입(float, real)인 경우. 정밀도 차이로 부동 소수점 비교가 일관되지 않은 결과를 만들 수 있어요.
  • 논리적 키 열이 대형 객체 타입(text, image, varbinary(max))인 경우. 키로 대형 객체를 사용하면 MERGE 성능이 크게 저하돼요.
  • 복합 논리적 키가 5개 이상의 열을 포함하는 경우. 긴 복합 키는 종종 설계 문제를 나타내며 MERGE 성능을 저하시킬 수 있어요.
  • 논리적 키가 테이블의 기존 기본 키를 재정의하는 경우. 대체 키가 의도적임을 확인해요. 커넥터는 더 이상 MERGE 작업에 기본 키를 사용하지 않아요.

이러한 경고 중 하나 이후 데이터 불일치가 관찰되면 주기적인 전체 재로드를 실행해 대상과 소스를 조정해요.

논리적 키에 영향을 주는 스키마 변경

커넥터는 CDC 캡처 인스턴스가 존재한 뒤에는 고유 또는 논리적 키 열의 스키마 진화를 추적하지 않아요. 논리적 키 열을 drop하거나 alter하는 것은 런타임에 감지되지 않아요.

  • 소스에서 논리적 키 열이 drop되면 해당 테이블의 복제가 실패해요. 복구하려면 테이블 복제를 다시 시작해요. 자세한 내용은 테이블 복제 다시 시작을 참고해요.
  • 소스에서 논리적 키 열의 이름이 바뀌면 구성이 여전히 이전 이름을 참조하므로 복제가 실패해요. JSON을 새 이름으로 업데이트하고 테이블 복제를 다시 시작해요.

테이블의 데이터 변경 추적 (Track data changes in tables)

커넥터는 소스 테이블의 현재 데이터 상태와 각 폴링 간격에서 감지된 변경 사항을 복제해요. 이 데이터는 대상 테이블과 같은 스키마에 생성된 저널(journal) 테이블에 저장돼요.

저널 테이블 이름은 <source_table_name>_JOURNAL_<timestamp>_<schema_generation> 형식으로 지정돼요. 여기서 <timestamp>는 소스 테이블이 복제에 추가되었을 때의 epoch 초 값이고, <schema_generation>은 소스 테이블의 스키마 변경마다 증가하는 정수예요. 그 결과 스키마 변경을 겪는 소스 테이블은 여러 저널 테이블을 가지게 돼요.

테이블을 복제에서 제거했다가 다시 추가하면 <timestamp> 값이 바뀌고 <schema_generation>은 1부터 다시 시작해요.

중요

Snowflake는 저널 테이블의 구조를 어떤 식으로든 변경하지 않을 것을 권장해요. 커넥터는 복제 과정의 일부로 대상 테이블을 업데이트하는 데 이 테이블들을 사용해요.

커넥터는 저널 테이블을 drop하지 않지만, 복제되는 모든 소스 테이블에 최신 저널을 사용하며 저널 위의 append-only 스트림만 읽어요. 스토리지를 회수하려면 다음을 할 수 있어요.

  • 언제든지 모든 저널 테이블을 truncate해요.
  • 복제에서 제거된 소스 테이블과 관련된 저널 테이블을 drop해요.
  • 활성 복제 테이블에 대해 최신 세대를 제외한 모든 저널 테이블을 drop해요.

예를 들어 커넥터가 소스 테이블 orders를 활발히 복제하고 이전에 테이블 customers를 복제에서 제거했다면 다음 저널 테이블이 있을 수 있어요. 이 경우 orders_5678_2를 제외한 전부를 drop할 수 있어요.

customers_1234_1
customers_1234_2
orders_5678_1
orders_5678_2

병합 작업 스케줄링 구성 (Configure scheduling of merge tasks)

커넥터는 웨어하우스를 사용해 변경 데이터 캡처(CDC) 데이터를 대상 테이블에 병합해요. Merge Journal to Destination이라는 이름의 프로세서가 이 작업을 트리거해요. 새 변경이 없거나 Merge Journal to Destination 큐에 대기 중인 새 FlowFile이 없으면 병합이 트리거되지 않고 웨어하우스는 자동 일시 중지(auto-suspension)가 가능해져요.

웨어하우스 비용을 제한하고 병합을 예약된 시간으로 제한하려면 Merge Task Schedule CRON 매개변수의 CRON 표현식을 사용해요. 이 표현식은 Merge Journal to Destination 프로세서에 도달하는 FlowFile을 조절하므로 지정된 기간 동안에만 병합이 트리거돼요. 커넥터는 이 일정을 UTC 표준시로 평가해요.

추가 정보와 예시는 Quartz 문서의 cron 트리거 튜토리얼을 참고해요.

플로우 실행 (Run the flow)

  • 캔버스를 마우스 오른쪽 버튼으로 클릭하고 Enable all Controller Services를 선택해요.
  • 가져온 프로세스 그룹을 마우스 오른쪽 버튼으로 클릭하고 Start를 선택해요. 커넥터가 데이터 수집을 시작해요.

더 알아보기 (Learn more)