블로그 이미지
fiadot_old

칼퇴근을 위한 게임 서버 개발 방법론에 대한 심도있는 고찰 및 성찰을 위한 블로그!

Rss feed Tistory
Technical Article 2007. 9. 28. 22:58

OLEDB의 Connection Pool에 대한 기본값 사용시 4가지 방법에 대한 수행속도 비교

MSSQL2K와 OLEDB Provider를 사용하여 VS6기반 ATL을 사용하여

OLEDB Wrapper를 구성해서 게임용 DB서버에서 사용하고 있었다.


올 중순부터  VS2005로 넘어오고 MSSQL2005로 바꾸면서,

기존의 네트워크 모듈부분을 뜯어고치면서

부분적으로 COM으로 구성하던 중,

DB쪽을 손보면서 OLEDB Wrapper에 대해 코드 리뷰를 하게 되었다.


기존 구조에서는 DBMS와의 연결은 단일하게 하고

Session을 DataSource에서 얻어와서 Accessor에서 사용하는 방식이였다.

즉, Stored Procedure나 query가 호출될때 session을 새로 생성하는 방식이다.

당시에는 MSSQL2K에서 Connection Pooling을 기본적으로 지원하기 때문에

Connection에 대해 크게 신경을 안쓰고 작성 했었는데, 이부분이 살짝 걸리긴 했다.

다른 방식에 대해서 성능 테스트를 해보지 못하고,

시간에 쫒겨서 만들었기 때문에  한번 검증해 보고자 테스트 코드를 만들어봤다.



환경은 Windows XP+SP2, MSSQL2005+SP1,

VS2005+SP1, MS OLEDB Provier for SQL Server 이다.

Connection스트링에 대한 옵션은 default값으로 하였다.

4가지 경우에 대한 성능 테스트를 진행해보았다.

공통적인 부분은 스레드 2개를 통해 SP(Stored Procedure)를 호출하는 consumer를 통해

recordset을 fetch하는 부분이다.

* TACCSSOR는 CCommand<CAccessor<CS_SP명> >을 상속한 클래스 인스턴스

 Case1>  Wizard를 통해 생성된 ATL OLEDB 소비자(consumer)를 그대로 사용 했다.
 ThreadFunc1()
 {
  TACCSSOR.OpenAll();  
 ...read...
 TACCSSOR.CloseDataSource();
 }

 Case2> DataSource 연결 1개. DataSource를 통해 Session 연결 1개.
 ThreadFunc2()
 {
 TACCESSOR.Open(미리 생성해둔 Session전달);
 ...read...
 TACCESSOR.Close();
}

 Case3> DataSource 연결 1개.  세션은 매번 생성
 ThreadFunc3()
 {
 CSession  session;
 _session.Open(DataSource); // DataSource를 인자로  Session생성
 TACCESSOR.Open(미리 생성해둔 Session전달);
 ...read...
 TACCESSOR.Close();
}

Case4> DataSource 연결1개, 자체 세션 풀 구성
ThreadFunc4()
{
CSession *pSession = GetSession();
 TACCESSOR.Open(*pSession);
 ...read...
 TACCESSOR.Close();
FreeSession(pSession);
}

즉, 기존에 구성했던 방식은 Case2에 해당된다.

과연 어떤 결과가 나올것인가?? 하하하~ 흥분 흥분 *--*;;

각각의 케이스에 대해 ThreadFunX를 1000회씩 실행한 결과이다.


const int DBPOOL_CASE = 1;   // 9567, 9511, 9560
const int DBPOOL_CASE = 2;   // 4388, 4402, 4375
const int DBPOOL_CASE = 3;   // 1162, 971, 977
const int DBPOOL_CASE = 4;   // 470, 530, 468

왜 이런 결과가 나온것일까??

Case1의 기본으로 생성해주는 consumer코드는 매번 DataSource를 연결하지만

Connection Pool이 구성되어 있다면 Case 2와 크게 차이가 없어야 맞다.

하지만, OpenAll 메소드 안에서 Propertie들에 대해 셋팅하는 부분과 OpenRowset을 호출하는

몇단계의 함수콜이 더 들어가게 된다. 이부분에서 시간을 소요하게 되는것이다.

Case2가 Case3보다 느린이유는 DataSource와 Session을 단일하게 사용하게 되면서

Bottle neck이 발생했다고 보는게 맞다.

(추가적으로 얻을수 있는 정보는 Session이 하나라도 자동적으로 Serialize되기때문에
 
 lock이 필요 없다는것. ^^)

Case3은 세션을 Datasource에서 가져오기 때문에 사용중인 세션에 SP를 호출하는

우를 범하지 않아서 계속 지역변수로써 Session을 생성하지만 더 빠른것이다.



Connection Pool을 직접적으로 체감하기 위해 스레드생성 이전부터 루프를 돌려 테스트를 진행해 보았다.

10회 // 4296, 3285, 1082, 588
20회 // 8801, 5908, 2118, 1089
50회 // 21883, 7544, 3257, 2585

잼있는 결과가 나온다.

자동으로 생성해준 코드가 DataSource와 Session을 미리 연결해놨을때 보다

훨씬 빠른 속도를 보여준다.

왜 이런 결과가 나온것일까?

DataSource와 Session을 단일하게만 사용했기때문에 Pool을 제대로 활용 못한 결과이다.

즉, DataSource와 Session을 매번 생성하는 Case 1의 경우 Pool을 제대로 활용했기때문에

Case1보다 빠른 성능을 보여준다.

하지만 Case3이 가장 빠를수 밖에 없는 이유는 Connection Pool도 활용하면서

선형적인 작업에는 Session Pool이 사용되는 구조이기 때문에 좋은 성능을 보여준다.

Case4는 Session을 미리 생성해두고 사용하기 때문에 지역변수가 Stack쌓이고 정리되는

과정을 줄여 속도를 높이고 있다.



결론적으로, DataSource는 하나로, Session은 필요할때(SP나 Query를 호출할때)

생성해주는게 좋은 성능을 보여준다.

게다가  Session또한 Pool로 구성하면 더욱 좋은 성능을 낼수 있다.



< vector를 이용해 Session Pool을 구현한 급조 소스 >






추가적으로 ...

<< Connection Pool에 대한 기본 개념  >>

Pool?  자료나 공간에 대해 미리 구성을 한뒤에 뽑아서 사용하고 다시 집어넣는 형식을 말함.

Connection Pool? 상호간의 연결에 대한 풀을 구성해놓고 접속을 재사용하는것.

MSSQL에서의 Connection Pool의 특징

1. Database Connection에 대한 풀.
  즉, OLEDB Provider 기준으로 보자면 CDataSrc에 해당한다.

2. 프로세스 단위로 구성
  각 풀은 프로세스 마다 풀을 구성하게 된다.

3. default값으로 Connection pooling 한다.

조건? Connection String부분이 blank하나까지도 동일해야 한다.
        (UDL파일을 사용하여 실수를 줄일수 있다)

,
TOTAL TODAY