원격 서버간 데이터 동기화 구현
한대성
MS SQL Server MVP
에이디컨설팅 책임 컨설턴트 | SQLLeader.com 운영자
이전에 다루었던 주제와 상당히 비슷한 내용인데, 조금 더 상세하게 설명하겠습니다.
[업무 요구 사항]
Ÿ 원본 서버(MSSQL or Oracle or Text File)의 데이터를 정기적으로 대상 서버로 동기화 해야 합니다.
Ÿ 원본의 데이터는
ž 신규로 발생되는 경우도 있고,
ž 기존 데이터가 변경되는 경우도 있으며,
ž 삭제되는 경우도 있습니다.
Ÿ 이러한 작업을 ETL Process로 처리하고자 합니다.
|
데이터베이스의 규모가 커지고 사용 범위가 확대되면서 이와 같이 외부 데이터 시스템의 데이터를 동기화 시키는 작업의 요구가 많아지고 있습니다. 복제나 로그 전달(Log Shipping), 미러링(Data Mirroring)과 같은 기능을 이용하여 데이터베이스 전체 또는 특정 테이블을 동기화 하는 것도 좋은 방안일 수 있습니다.
하지만, 저장 공간의 문제나 구축 및 관리 상의 문제로 단순히 SSIS 또는 DTS와 같은 ETL 패키지를 이용하여 동기화 하는 작업을 구현하는 것이 간단할 수도 있습니다.
본 글에서는 SSIS를 이용하여 이러한 작업을 구현하는 예제와 구현 시 고려해야 할 사항들을 살펴보도록 하겠습니다.
1. 원본 및 대상 테이블 생성
Source Instance: localhost
USE TEMPDB
GO
IF EXISTS (SELECT * FROM SYSOBJECTS WHERE NAME='SOURCE' AND XTYPE = 'U')
DROP TABLE SOURCE
GO
--원본 테이블
CREATE TABLE SOURCE
(
SEQ INT IDENTITY,
VAL VARCHAR(10) DEFAULT('AAA'),
CONSTRAINT PK_SOURCE PRIMARY KEY(SEQ)
)
GO
--임의 데이터 발생
SET NOCOUNT ON
DECLARE @I AS INT
SET @I = 1
WHILE @I<=10000
BEGIN
INSERT SOURCE DEFAULT VALUES
SET @I = @I + 1
END
SET NOCOUNT OFF
GO
|
Target Instance: localhost\SS2005
USE TEMPDB
GO
IF EXISTS (SELECT * FROM SYSOBJECTS WHERE NAME='TARGET' AND XTYPE = 'U')
DROP TABLE TARGET
GO
--대상 테이블
CREATE TABLE TARGET
(
SEQ INT,
VAL VARCHAR(10),
CONSTRAINT PK_TARGET PRIMARY KEY(SEQ)
)
GO
|
2. SSIS 패키지 생성
1) 빈 패키지를 생성한 후, SourceDB와 TargetDB에 대한 OLE DB 연결을 추가합니다.
2) 제어 흐름 영역에 데이터 흐름 작업을 추가한 후, 더블 클릭해서 데이터 흐름 작업 영역을 엽니다. 데이터 흐름 영역에서 OLE DB 원본을 추가한 후, 다음과 같이 설정합니다.
데이터 액세스 모드(A) 부분에서 테이블 또는 뷰를 선택한 후, 원본 테이블을 사용해도 되지만 가급적이면 위와 같이 SQL 명령을 선택한 후, 작업에 필요한 최소한의 열만 선택하도록 합니다. 원본 데이터가 위와 같이 SQL Server의 테이블에 있는 데이터인 경우, WITH (NOLOCK) 옵션을 사용하여, 원본의 데이터를 읽어오는 중에 불필요한 블로킹이 발생하지 않도록 설정합니다.
3) 조회 변환을 추가한 후, 원본과 연결하고 다음과 같이 설정합니다.
본 단계에서도 필요한 열에 대해서만 가지고 오도록 설정하기 위해 SQL 쿼리를 이용하도록 합니다.
4) 열 탭에서 Source 테이블과 Target 테이블의 키 열인 Seq 열을 연결시키고, 조회 열의 Val 열을 선택한 후, 출력 별칭을 Val에서 Val2로 변경합니다.
원본의 Seq열 데이터에 대해 조회 테이블에 이 값이 있는지 확인합니다. 이 때 주의해야 할 것은 반드시 Key 열에 대해서만 연결을 지정해야 하며, 다른 열은 삭제해줘야 합니다. (SSIS에서는 열 이름이 동일할 경우, 자동으로 연결을 시키기 때문에 Val열에 대해서도 연결선이 생성됩니다. Val열은 Key 열이 아니기 때문에 삭제를 해 줘야 합니다.)
추가로 이 부분에 대해 조금 구체적으로 설명하겠습니다.
Source
Seq
|
Val
|
1
|
AAA
|
2
|
BBB
|
3
|
CCC
|
|
Target
|
Source 테이블의 키 열인 Seq 열의 값을 Target 테이블의 Seq 열과 조회 작업을 수행합니다. Seq가 1, 2인 경우에는 매치가 되기 때문에 조회가 성공으로 처리됩니다. Seq가 3인 경우에는 Target에 해당 데이터가 없기 때문에 조회가 실패로 처리가 됩니다. 결국 조회가 실패라는 것은 신규 데이터라는 것이기에 Target 테이블에 Insert 해 주면 됩니다.
조회된 데이터인 첫 번째, 두 번째 행에 대해서는 하위 열이 변경되었는지를 판단해야 합니다. 이러한 판단 작업을 수행하기 위해 위의 그림에서와 같이 조회 테이블(=Target)의 Val열을 추가해서 출력시키는 것입니다.
5) 오류 출력 구성(G)를 클릭한 후, 오류의 방법을 행 리디렉션으로 변경합니다.
6) 데이터 흐름 영역에 OLE DB 대상을 추가한 후, 조회 작업의 적색 선을 연결하고 OLE DB 대상을 다음과 같이 설정합니다.
대상 테이블에 데이터를 입력하는 동안 조회 작업이나 다른 작업이 없는 경우에는 테이블 잠금(O) 옵션을 체크해 주는 것이 성능상 유리합니다. 만약, 신규로 처리되는 건이 많고 대상 테이블이 계속 사용된다면 테이블 잠금 옵션을 해제하고 필요에 따라 최대 삽입 커밋 크기(M)의 값을 0 (모든 데이터 일괄 처리) 대신 다른 값으로 설정하도록 합니다.
테이블을 지정한 후, 매핑 탭을 클릭해서 입력 데이터에 대해 매핑을 설정합니다.
7) 이제 변경된 데이터에 대한 업데이트 작업을 수행하는 단계를 설정합니다. 데이터 흐름 영역에 조건부 분할을 추가한 후, 다음과 같이 조회 작업의 녹색 선과 연결하고 조건부 분할의 편집기에서 조건을 설정합니다.
8) OLE DB 명령을 추가한 후, 조건부 분할 변환과 연결합니다. 이 때 출력 이름을 선택하는 창이 나타나는데, 기본 출력 대신 『수정 데이터』 경로를 선택합니다.
9) OLE DB 명령의 편집 창에서 연결 관리자를 TargetDB로 설정하고 구성 요소 속성 탭의 SqlCommand 속성에
UPDATE Target SET Val = ? WHERE Seq = ?
을 입력합니다.
10) 열 매핑 탭에서 Seq 열을 Param_1, Val 열을 Param_0에 매핑 시킵니다.
오른쪽에 나타나는 사용 가능한 대상 열은 앞의 쿼리에서 ?로 표시한 대로 나타나며 쿼리의 첫 번째가 Param_0, 두 번째가 Param_1 등으로 매핑됩니다.
만약 매핑 해야 할 열이 상당히 많은 경우에는 정확한 매핑이 되기 어려우며, 필요한 경우 가공 작업이나 변환 작업이 수행되어야 하기 때문에 가능한 한 위의 경우와 같이 직접 쿼리를 지정하는 대신 별도의 저장 프로시저를 만든 후 이 프로시저를 이용할 것을 권장합니다. 저장 프로시저를 사용할 경우, 위와 같이 Param_0, Param_1 로 나타나는 대신 해당 저장 프로시저의 입력 매개 변수가 표시됩니다.
자, 이제 한 번 패키지를 수행해 보도록 하겠습니다.
현재는 Source 테이블에만 10,000개의 데이터가 들어있기 때문에 Target 테이블에 모두 이관될 것입니다.
이제 Source 테이블에 다음과 같이 500개의 신규 데이터를 발생시키고, 또한 기존 데이터의 일부를 수정해 보도록 하겠습니다.
Source Instance: localhost
--신규 데이터 발생
SET NOCOUNT ON
DECLARE @I AS INT
SET @I = 1
WHILE @I<=500
BEGIN
INSERT SOURCE DEFAULT VALUES
SET @I = @I + 1
END
SET NOCOUNT OFF
GO
--기존 데이터 수정
UPDATE SOURCE
SET VAL = 'BBB'
WHERE SEQ BETWEEN 1001 AND 1300
GO
--(300개 행 적용됨)
|
신규 데이터 500개가 추가되며, 기존 데이터 중 300개가 수정된 것을 확인할 수 있습니다.
여기까지 UpSert(Update + Insert) 작업이 구현되었습니다. 하지만, 다음과 같은 성능과 관련된 사항을 고려해 볼 수 있습니다.
다음 그림은 Target 테이블에 Update 작업이 수행될 때 프로파일러를 이용해서 수행되는 쿼리를 확인한 결과입니다.
수정을 해야 할 경우, 개별 행 단위로 처리가 됩니다. 즉, 위의 경우에는 Target 테이블에 300번의 Update 문이 수행되었습니다. 만약 신규 입력된 데이터에 비해 변경 데이터가 거의 없는 경우에는 지금과 같은 형태로 구현해도 성능상 큰 문제가 없습니다. 하지만 수정 데이터가 많은 경우에는 대상 테이블에 엄청난 부하를 발생시킬 수 있습니다.
이러한 경우에는 OLE DB 명령을 수행하는 대신 다른 방법을 사용해야 합니다.
여러 방법이 있겠지만, 필자는 다음과 같은 형태로 생각해 보았습니다.
업데이트를 수행해야 하는 데이터를 개별 행 단위로 업데이트를 수행하는 대신, 임시 테이블에 저장시켜 놓고, 변환 작업이 끝난 후 이 임시테이블의 데이터를 이용하여 Target 테이블에 일괄 업데이트를 수행하는 방식입니다.
우선, 현재의 데이터 흐름 작업을 수행하기 전에 임시 테이블을 만드는 SQL 실행 작업을 추가합니다.
[SQLStatement]
IF EXISTS (SELECT * FROM sysobjects WHERE xtype = 'u' AND name = 'tmpupdTarget')
DROP TABLE tmpupdTarget
GO
CREATE TABLE tmpupdTarget
(
Seq INT,
Val2 VARCHAR(10)
)
GO
|
다음 단계로 진행하기 전에 쿼리 분석기에서 위의 쿼리를 수행하여 이 임시 테이블을 생성시켜 놓습니다.
(만약 테이블이 없는 상태이며, 패키지의 작업 개체에서 DelayValidation 옵션이 False인 경우, 패키지가 시작될 때 데이터 흐름 작업에서 대상 테이블이 없다고 판단하여 유효성 검사 실패 에러가 발생하게 됩니다. 따라서, 미리 테이블을 만들어 놓거나, 아니면 데이터 흐름 작업의 DelayValidation 속성 값을 True로 변경시킨 후 패키지를 실행해야 합니다.)
또한, 데이터 흐름 작업 다음에 이 임시 테이블을 이용해서 업데이트를 수행하는 SQL 실행 작업을 추가하고 다음과 같이 설정합니다.
[SQLStatement]
UPDATE A
SET A.Val = B.Val2
FROM Target A JOIN tmpupdTarget B ON A.Seq = B.Seq
GO
|
이제, 데이터 흐름 작업 내에서 OLE DB 명령 대신 OLE DB 대상을 추가하고 다음과 같이 tmpupdTarget 테이블을 선택하고, 매핑 탭에서 Seq 열과 Val2 열에 대해 매핑을 설정합니다.
(Head1ton의 말 : Val2 에 Val2로 매칭하니 업데이트가 안된다. 왼쪽의 Val로 매칭시켜주니깐 된다..-_-;)
자, 이제 다시 쿼리 분석기에서 임의의 수정 데이터를 만든 후에 패키지를 실행시켜서 정상적으로 수행되는지 확인합니다.
프로파일러로 수행된 쿼리를 확인하면 다음과 같습니다.
이제 삭제 단계가 남았습니다. 본 단계는 앞의 작업들과 상당히 유사하기 때문에 간단히 설명 만으로 대체하겠습니다.
(첨부된 예제 파일에는 구현해 놓았습니다.)
삭제 단계는 Insert나 Update와는 반대로 Target 테이블이 원본이 되며 Source 테이블이 조회 테이블이 됩니다. 이 때 조회 쿼리는 키 열인 Seq 열에 대해서만 비교하면 됩니다. Target 테이블의 데이터 중 Source 테이블의 데이터가 조회되지 않는 경우가 삭제된 데이터이기 때문에 조회가 실패한 데이터를 별도의 임시 테이블로 적재하고, 이 임시테이블의 데이터를 이용해서 Target 테이블의 데이터를 삭제하도록 수행합니다.
[참고 글]
http://www.sqlleader.com/mboard.asp?exec=view&strBoardID=SS2005SSIS&intSeq=779
http://www.sqlleader.com/mboard.asp?exec=view&strBoardID=SS2005SSIS&intSeq=682
http://www.sqlleader.com/mboard.asp?exec=view&strBoardID=SS2005SSIS&intSeq=409