'Windows Azure Platform/SQL Azure'에 해당되는 글 2건

  1. 2011/01/05 SqlBulkCopy 클래스를 SQL Azure에서 사용하는 방법
  2. 2011/01/03 구관이 명관 - OSQL과 SQL Azure의 만남

최근 Windows Azure 기반 Social App을 개발하면서 한 가지 문제에 봉착했었습니다. 인위적으로 생성해낸 수십만건의 레코드를 어떤 방법으로 SQL Azure에 Throttling 현상 없이 (SQL Azure 스스로가 성능 향상을 목적으로 과도한 부하를 일으키는 연결이나 불필요하게 장시간 연결되어있는 연결을 임의로 cut하는 동작) 안전하게 모든 데이터를 게시할 수 있는지에 관한 문제였습니다. 그러던 중 SQL Bulk Copy 클래스를 SQL Azure에서 사용하기 위하여 여러가지 시도를 해보던 끝에 아래와 같은 기본적인 지침을 얻을 수 있었습니다.

1. 최상의 결과를 내기 위해서는 SQL Azure 대상 테이블과 원본 테이블 사이의 정의를 가능한한 일치시켜야 한다.

저의 경우, 인위적으로 생성한 수십만건의 레코드를 SQL Azure의 테이블에 게시하기 위하여 여러가지 시도를 해보았지만 데이터 중복에 관한 처리를 정확히 할 수 없어 상당한 시행착오를 겪었습니다. 결국 찾게 된 방법은 ADO.NET이 기본으로 제공하는 In-Memory RDBMS 시스템을 활용하는 것으로, SQL Azure에 게시하면서 유효성 검사를 하지 않고 In-Memory RDBMS에서 미리 유효성 검사를 한 후 최종적으로 산출된 데이터만을 게시하도록 하는 것이었습니다.

아래의 코드는 history_id, template_id, parameter_id, parameter_value라는 4개의 column을 정의하는 테이블로, history_id, template_id, parameter_id column을 한 번에 primary key로 지정하는 방법을 C# 코드로 보여주고 있습니다.

DataTable table = new DataTable();
table.Columns.Add("history_id", typeof(int)).AllowDBNull = false;
table.Columns.Add("template_id", typeof(string)).AllowDBNull = false;
table.Columns.Add("parameter_id", typeof(string)).AllowDBNull = false;
table.Columns.Add("parameter_value", typeof(string)).AllowDBNull = false;
table.Constraints.Add("history_id_pk",
    new DataColumn[] { table.Columns["history_id"], table.Columns["template_id"], table.Columns["parameter_id"] },
    true);

SQL Azure에도 위와 비슷한 형태의 테이블이 이미 게시되어있는 상황에서, SQL Azure에 데이터를 삽입하거나 업데이트 - 또는 - 삭제하면서 유효성 검사가 일어나도록 기다리지 않고 로컬에서 간단한 유효성 검사를 미리 수행하여 네트워크 부하를 최소화하고 작업 속도를 개선할 수 있었습니다.

2. Windows Server 2003 SP2 사용자 필독

클라이언트 - 혹은 - 관리 도구 차원에서 SqlBulkCopy 클래스를 사용하려는 PC의 환경이 Windows Server 2003 SP2인 경우 SQL Azure로의 연결이 아무런 까닭없이 성립되지 않는 이상한 현상을 보일 수 있습니다. 이 경우, 시스템에 KB977291 Hotfix (http://support.microsoft.com/kb/977291/en-us)가 설치되어있는지 확인하시고, 설치되어있지 않은 경우 해당 핫 픽스를 설치한 후 다시 시도해야 합니다.

3. SqlBulkCopy 유틸리티를 사용할 때는 가능한한 명시적으로 사용할 것

애석하게도, 자동화 도구는 편의를 위하여 제공되는 것이지만 편의를 사용자에게 제공하기 위해서는 적정한 설정이 뒷받침되어야만 합니다. SqlBulkCopy 클래스가 그런 유형인데, ColumnMappings 속성, BatchSize 속성, BulkCopyTimeout 속성, DestinationTableName 속성을 정확히 이해하고, WriteToServer 메서드를 호출하기 전에 설정해야 합니다.

SqlBulkCopy bulkCopy = new SqlBulkCopy(conn);
bulkCopy.ColumnMappings.Add("history_id", "history_id");
bulkCopy.ColumnMappings.Add("template_id", "template_id");
bulkCopy.ColumnMappings.Add("parameter_id", "parameter_id");
bulkCopy.ColumnMappings.Add("parameter_value", "parameter_value");

bulkCopy.BatchSize = 100;
bulkCopy.BulkCopyTimeout = 100;
bulkCopy.DestinationTableName = "tb_design_image_history";

bulkCopy.SqlRowsCopied += new SqlRowsCopiedEventHandler(bulkCopy_SqlRowsCopied);
bulkCopy.WriteToServer(table);

위 코드에서 특별히 중요한 것은 ColumnMappings, BatchSize, DestinationTableName 속성에 관한 부분입니다. ColumnMappings를 사용하여 원본 데이터 소스에서 대상 데이터 소스로 복사할 때의 위치를 결정해야 합니다. 그리고 BatchSize는 SQL Azure가 수용할 수 있는 범위의 값을 사용해야 하며 개인적인 테스트 결과로는 100이 안전한 값인듯합니다. 기본값을 사용하려고 하면 SQL Azure가 연결을 거부할 수 있습니다. 그리고 DestinationTableName 속성에서 대상 테이블 이름이 정확히 지정되어야 올바르게 작동합니다.

마지막으로 WriteToServer 메서드는 상당히 유연한 것입니다. 이미 로컬에 캐시된 데이터 테이블과 그 행의 배열은 물론, 아직 연결이 열린 상태인 다른 데이터 소스에 대한 IDataReader 객체의 사용을 허용합니다. 정의만 서로 정확히 일치하고 맞아떨어진다면 동기화를 비교적 손쉽고 간편하게 할 수 있습니다.

4. 결론

SqlBulkCopy는 개별적으로 INSERT 명령어를 SQL Azure에 실행하는 것보다 훨씬 빠르게 동작했습니다. 덕분에 40여만건 이상의 레코드를 무사히 모두 복제할 수 있었습니다. 하지만 한 가지 중요한 점이 있는데, 개별적으로 INSERT 명령어를 실행하는 것과 달리 SqlBulkCopy 클래스는 중간에 발생하는 데이터베이스 오류를 핸들링할 수 있는 capability가 없으며 문제가 발생하면 작업이 중간에 끊기게 되므로 이를 예방하려면 Transaction을 사용하여 처리 전후 과정을 보완하는 것이 필요합니다.

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)
저 개인적으로는 오래된 명령줄 인터페이스 기반의 툴을 매우 사랑합니다. 가끔 GUI로 채워지지 않는 부족함을 명령줄 인터페이스 기반의 툴을 통하여 채울 수 있기 때문입니다. 비유하자면 효자손과 같은 존재이지요. :-)

오늘은 그 중에서도 SQL Azure를 관리하기 위한 목적으로 OSQL 도구를 사용하는 방법을 설명하고자 합니다. 요즈음은 SQL Management Studio 덕분에 그 수요가 많이 줄어들었지만, 아직 OSQL 도구가 유용하게 사용될 수 있는 여지는 매우 많습니다. 저의 경우, Sync Framework와 SQL Server Agent를 사용하여 쉽게 동기화할 수 있는 상황이 아닌 이기종 데이터베이스간 데이터 마이그레이션 시나리오에서 매우 유용하게 사용하고 있습니다. C#을 이용하여 실제 데이터를 insert하는 구문을 SQL 스크립트로 덤프를 생성하여 이것을 OSQL 도구에 매개 변수로 전달하는 방법을 선호합니다. BLOB이나 CLOB, 개행 문자를 포함하지 않는 데이터로 구성되어있는 경우 이 방법은 매우 유용한 것 같습니다.

그렇다면 OSQL을 통해서 SQL Azure에 접속하려면 어떻게 해야 할까요? 방법은 다음과 같습니다.

osql -S<your_server>.database.windows.net -U<your_database_id>@<your_server> -P<your_password> -d<database_name>

SQL Azure에 접속하는 방법은 일상적으로 우리가 사용했던 SQL Express Edition이나 SQL Server에 접속하는 방법과 조금 다릅니다. 각 스위치별로 필요한 항목들을 하나씩 살펴보겠습니다.

  • -S 스위치: 접속 대상 서버를 지정합니다. SQL Azure의 호스트 이름은 주로 xxxx.database.windows.net과 같은 형태로 되어있고, 이 부분의 정확한 주소는 SQL Azure Portal에서 확인할 수 있습니다.
  • -U 스위치: 사용자 ID를 지정합니다. 통상 사용하는 SQL Server와는 다르게, 사용자 이름 뒤에 서버 이름을 명시적으로 @ 기호와 함께 지정해야 합니다. 예를 들어, <your_server>에 들어가는 식별자가 abcd라고 한다면 <your_database_id>@abcd가 이 스위치에 지정되어야 합니다.
  • -P 스위치: 사용자 비밀 번호를 지정합니다. 띄어쓰기가 있는 비밀 번호의 경우 -P"<your_password>"와 같은 형식으로 따옴표를 이용하여 비밀 번호 문자열 주변을 포장하여 인수로 전달합니다.
  • -d 스위치: -D 스위치가 아닌 -d 스위치로, 대/소문자 구분에 유의합니다. 이 부분에서는 사용하려는 데이터베이스 이름을 지정해야합니다. SQL Azure의 특성 상 한번 연결이 완료되면 다른 데이터베이스로는 문맥 전환을 할 수 없으며, 따라서 USE 문의 사용은 허용되지 않습니다.

위와 같이 지정하여 성공적으로 접속하였다면 프롬프트가 나타날 것입니다. 하지만 대부분의 경우 방화벽 설정에 의하여 연결이 차단될 수 있습니다. 이 때에는 SQL Azure Portal로 접속하여 현재 여러분이 접속을 시도하는 IP 주소를 SQL Azure에서 접속할 수 있도록 허용해야 합니다. 여기서 주의할 것은, NAT나 인터넷 공유기와 같이 하나의 외부 IP 주소를 여러 PC가 공유하는 환경에서 SQL Azure를 접속하려는 경우 보안 상 위험이 발생할 수 있다는 점입니다.

이제 대량의 query문을 실행하기 위한 스위치를 하나 더 알아보겠습니다. 바로 -i 스위치입니다. 이 스위치를 이용하여 실행하려는 SQL 문장이 포함된 스크립트 파일을 지정하고, -o 스위치를 사용하여 실행 결과를 텍스트 파일로 갈무리할 수도 있습니다.

전체 사용 예시는 다음과 같습니다.

osql -Sabcdefghi0.database.windows.net -Urkttu@abcdefghi0 -Pmypassword -dmydatabase -itest.sql -ooutput.txt

위와 같이 명령어를 실행하면 test.sql 파일의 내용을 모두 실행하고 그 결과를 output.txt에 써내려갈 것입니다.

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)