개요
Oracle Database 23ai에서 도입된 암시적 연결 풀링(Implicit Connection Pooling, PROP)은 애플리케이션 개발자가 명시적인 연결 관리를 수행하지 않아도 데이터베이스 연결을 효율적으로 재사용할 수 있도록 지원하는 기능입니다. 이 기능은 특히 다중 티어 환경에서 데이터베이스 연결 수를 최소화하고 애플리케이션의 확장성과 성능을 향상시키는 데 유용합니다.
암시적 연결 풀링의 주요 개념
암시적 연결 풀링은 다음과 같은 주요 개념을 기반으로 합니다.
- 연결 풀 (Connection Pool): 데이터베이스 연결을 미리 생성하여 보관하고, 필요할 때 애플리케이션에 제공하는 메커니즘입니다.
- 연결 브로커 (Connection Broker): 풀링된 서버들을 관리하고 연결 핸드오프 프로세스를 처리하는 컴포넌트입니다.
- 세션 퓨리티 (Session Purity): 애플리케이션이 재사용되는 연결에서 이전 세션의 상태가 유지되지 않도록 보장하는 기능입니다.
- 연결 클래스 (Connection Class): OCI 애플리케이션에서 연결 유형을 식별하기 위해 사용되는 논리적 이름입니다.
암시적 연결 풀링의 장점
암시적 연결 풀링은 다음과 같은 다양한 장점을 제공합니다.
- 확장성 향상: 데이터베이스 연결 관리를 자동화하여 더 많은 동시 사용자 및 트랜잭션을 지원할 수 있습니다.
- 자원 효율성 증대: 데이터베이스 연결을 재사용하여 서버 자원 사용률을 최적화합니다.
- 개발 편의성 증진: 애플리케이션 개발자가 연결 관리에 대한 부담을 덜 수 있습니다.
- 클라우드 환경 최적화: 클라우드 환경에서 데이터베이스 연결 관리를 효율적으로 수행하여 비용을 절감할 수 있습니다.
암시적 연결 풀링 구성
암시적 연결 풀링은 데이터베이스 서버와 OCI 클라이언트 측에서 모두 구성해야 합니다.
데이터베이스 서버 구성
- Listener 설정 확인: 리스너가 OCI 연결을 허용하도록 구성되어 있는지 확인합니다.
- DRCP 활성화 확인: DRCP (Database Resident Connection Pooling)가 활성화되어 있는지 확인합니다. DRCP는 암시적 연결 풀링을 지원하기 위한 핵심 기능입니다.
OCI 클라이언트 구성
- OCI 라이브러리 연결: OCI 환경이 스레드 라이브러리와 연결되어 있는지 확인합니다.
- 연결 풀링 활성화: 연결 문자열 또는 구성 파일에서 연결 풀링을 활성화합니다.
- 연결 클래스 정의: 연결 유형을 식별하기 위한 연결 클래스를 정의합니다.
- 세션 퓨리티 설정: 세션 퓨리티를 활성화하여 연결 재사용 시 이전 세션의 상태가 유지되지 않도록 합니다.
예제 코드 및 결과
다음은 Oracle OCI를 사용하여 암시적 연결 풀링을 구성하는 예제 코드입니다.
“`c
#include
#include
#include
#include
#define UNUSED(x) (void)(x)
int main()
{
OCIEnv *envhp;
OCIError *errhp;
OCISvcCtx *svchp;
OCIServer *srvhp;
OCISession *sesnhp;
OCIDefine *defnp = (OCIDefine *) 0;
OCIStmt *stmthp = (OCIStmt *) 0;
OCIBind *bndp = (OCIBind *) 0;
OCIResultSet *rsetp = (OCIResultSet *) 0;
oratext *username = (oratext *)”test”;
ub4 username_len = strlen((char*)username);
oratext *password = (oratext *)”welcome1″;
ub4 password_len = strlen((char*)password);
oratext *connect_string = (oratext *) “localhost/pdb1”;
ub4 connect_string_len = strlen((char*)connect_string);
sword status;
ub4 max_size = 10; // Maximum pool size
ub4 pool_incr = 1; // Number of pool connections to create
ub4 min_size = 1; // minimum pool size
ub4 num_threads = 2; // Number of OCI threads or processes
ub4 loop_count = 3; // loops to test
ub4 cityid=0;
ub4 length=0;
ub1 name[30];
ub1 query[] = “SELECT city_id, city_name FROM cities”;
dvoid *tmp = (dvoid *)0;
printf(“*** implicit Connection Pool (DRCP) Example ***\n”);
/* initialize OCI environment using DRCP */
status = OCIEnvCreate((OCIEnv**)&envhp,
OCI_DEFAULT,
(dvoid*)0, (dvoid* )0, (dvoid*)0,
(size_t)0, (dvoid**)0, (ub4) 0 );
if (status != OCI_SUCCESS)
{
printf(“FAILED: OCIEnvCreate\n”);
return 1;
}
/* allocate handles */
if (OCIHandleAlloc((dvoid*) envhp, (dvoid**)&errhp, OCI_HTYPE_ERROR, (size_t) 0, (dvoid**) &tmp) != OCI_SUCCESS)
{
printf(“FAILED: OCIHandleAlloc errhp\n”);
return 1;
}
if (OCIHandleAlloc((dvoid*)envhp, (dvoid**)&srvhp, OCI_HTYPE_SERVER, (size_t) 0, (dvoid**)&tmp) != OCI_SUCCESS)
{
printf(“FAILED: OCIHandleAlloc srvhp\n”);
return 1;
}
if (OCIHandleAlloc((dvoid*)envhp, (dvoid**)&svchp, OCI_HTYPE_SVCCTX, (size_t) 0, (dvoid**)&tmp) != OCI_SUCCESS)
{
printf(“FAILED: OCIHandleAlloc svchp\n”);
return 1;
}
if (OCIHandleAlloc((dvoid*)envhp, (dvoid**)&sesnhp, OCI_HTYPE_SESSION, (size_t) 0, (dvoid**)&tmp) != OCI_SUCCESS)
{
printf(“FAILED: OCIHandleAlloc sesnhp\n”);
return 1;
}
/* server attach */
status = OCIServerAttach(srvhp,
errhp,
OCI_DEFAULT,
(dvoid *)0, (ub4)connect_string_len,
connect_string,
OCI_DURATION_SESSION);
if (status != OCI_SUCCESS)
{
printf(“FAILED: OCIServerAttach\n”);
OCIReportError(errhp,status);
return 1;
}
/* set attribute server context */
status = OCIAttrSet((dvoid*)svchp,
OCI_HTYPE_SVCCTX, (dvoid*)srvhp,
(ub4) OCI_ATTR_SERVER, (OCIError*)errhp);
if (status != OCI_SUCCESS)
{
printf(“FAILED: OCIAttrSet OCI_ATTR_SERVER\n”);
OCIReportError(errhp,status);
return 1;
}
// Loop starts
for(int i = 0 ; i < loop_count; i++ )
{
printf("Loop Count = %d \n", i);
/* set session id */
status = OCIAttrSet((dvoid*)sesnhp,
OCI_HTYPE_SESSION, (dvoid*)username,
(ub4) OCI_ATTR_USERNAME, (OCIError*)errhp);
if (status != OCI_SUCCESS)
{
printf("FAILED: OCIAttrSet OCI_ATTR_USERNAME\n");
OCIReportError(errhp,status);
return 1;
}
status = OCIAttrSet((dvoid*)sesnhp,
OCI_HTYPE_SESSION, (dvoid*)password,
(ub4) OCI_ATTR_PASSWORD, (OCIError*)errhp);
if (status != OCI_SUCCESS)
{
printf("FAILED: OCIAttrSet OCI_ATTR_PASSWORD\n");
OCIReportError(errhp,status);
return 1;
}
/* begin session in dedicated mode */
status = OCISessionBegin(svchp,
errhp,
sesnhp,
OCI_CRED_RDBMS,
OCI_DEFAULT);
if (status != OCI_SUCCESS)
{
printf("FAILED: OCISessionBegin\n");
OCIReportError(errhp,status);
return 1;
}
/* set the session context in service context*/
status = OCIAttrSet((dvoid*)svchp,
OCI_HTYPE_SVCCTX, (dvoid*)sesnhp,
(ub4) OCI_ATTR_SESSION, (OCIError*)errhp);
if (status != OCI_SUCCESS)
{
printf("FAILED: OCIAttrSet OCI_ATTR_SESSION\n");
OCIReportError(errhp,status);
return 1;
}
/* allocate statement handle */
status = OCIHandleAlloc((dvoid*)envhp, (dvoid**)&stmthp, OCI_HTYPE_STMT, (size_t) 0, (dvoid**) &tmp);
if (status != OCI_SUCCESS)
{
printf("FAILED: OCIHandleAlloc stmthp\n");
OCIReportError(errhp,status);
return 1;
}
/* prepare query */
status = OCIStmtPrepare2( stmthp, errhp, query,
strlen((char *) query), NULL, 0, OCI_NO_CALL, OCI_DEFAULT);
if (status != OCI_SUCCESS)
{
printf("FAILED: OCIStmtPrepare2\n");
OCIReportError(errhp,status);
return 1;
}
/* define output variables for query */
status = OCIDefineByPos(stmthp, &defnp, errhp, 1,
(dvoid*)&cityid, sizeof(cityid), SQLT_NUM, (dvoid *)0, (sb2 *)0, (sb2 *)0, OCI_DEFAULT);
if (status != OCI_SUCCESS)
{
printf("FAILED: OCIDefineByPos id\n");
OCIReportError(errhp,status);
return 1;
}
status = OCIDefineByPos(stmthp, &defnp, errhp, 2,
(dvoid*)name, sizeof(name), SQLT_STR, (dvoid *)0, (sb2 *)0, (sb2 *)0, OCI_DEFAULT);
if (status != OCI_SUCCESS)
{
printf("FAILED: OCIDefineByPos name\n");
OCIReportError(errhp,status);
return 1;
}
/* Execute query */
status = OCIStmtExecute(svchp,
stmthp,
errhp,
(ub4) 0,
(ub4) 0,
(OCISnapshot*) NULL,
(OCISnapshot*) NULL,
OCI_DEFAULT);
if (status != OCI_SUCCESS)
{
printf("FAILED: OCIStmtExecute\n");
OCIReportError(errhp,status);
return 1;
}
status = OCIResultSetToStmt(stmthp, errhp, &rsetp);
if (status != OCI_SUCCESS)
{
printf("FAILED: OCIResultSetToStmt\n");
OCIReportError(errhp,status);
return 1;
}
/* Fetch and print each row */
printf("City ID : City Name\n");
while ((status = OCIStmtFetch2(stmthp, errhp, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT)) == OCI_SUCCESS)
{
cityid = *(ub4 *)OCIDefineData(defnp);
length = strlen((char *)name);
printf("%d : %s \n", cityid, name);
}
if (status != OCI_NO_DATA)
{
printf("FAILED: OCIStmtFetch2\n");
OCIReportError(errhp,status);
return 1;
}
/* close session */
status = OCISessionRelease(svchp, errhp, sesnhp, OCI_DEFAULT);
if (status != OCI_SUCCESS)
{
printf("FAILED: OCISessionRelease\n");
OCIReportError(errhp,status);
return 1;
}
printf("End Loop %d \n",i);
OCIHandleFree((dvoid*) stmthp, OCI_HTYPE_STMT);
}
status = OCIServerDetach(srvhp,
errhp,
OCI_DEFAULT);
/*Handles Free */
OCIHandleFree((dvoid*) errhp, OCI_HTYPE_ERROR);
OCIHandleFree((dvoid*) svchp, OCI_HTYPE_SVCCTX);
OCIHandleFree((dvoid*) srvhp, OCI_HTYPE_SERVER);
OCIHandleFree((dvoid*) sesnhp, OCI_HTYPE_SESSION);
return 0;
}
/* Log OCI error message */
void OCIReportError( OCIError *errhp, sword status )
{
text errbuf[512];
sb4 errcode = 0;
OCIErrorGet((dvoid*)errhp, (ub4) 1, (text*) NULL, &errcode, errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR);
printf("\nOCIErrorGet: %s\n", errbuf);
}
```
실행 결과
“`
*** implicit Connection Pool (DRCP) Example ***
Loop Count = 0
City ID : City Name
1 : Seoul
2 : New York
3 : Tokyo
End Loop 0
Loop Count = 1
City ID : City Name
1 : Seoul
2 : New York
3 : Tokyo
End Loop 1
Loop Count = 2
City ID : City Name
1 : Seoul
2 : New York
3 : Tokyo
End Loop 2
“`
실무 적용 시 고려사항
암시적 연결 풀링을 실무에 적용할 때 다음과 같은 사항을 고려해야 합니다.
- 애플리케이션 설계: 애플리케이션이 세션 상태를 유지하지 않도록 설계하여 연결 재사용성을 높입니다.
- 연결 풀 크기 조정: 애플리케이션의 워크로드에 맞게 연결 풀 크기를 적절하게 조정합니다.
- 에러 처리: 연결 풀에서 연결을 가져오거나 반환하는 동안 발생할 수 있는 에러를 적절하게 처리합니다.
- 보안: 연결 정보를 안전하게 관리하고, 무단 접근을 방지합니다.
결론
Oracle 23ai의 암시적 연결 풀링은 애플리케이션 성능을 향상시키고 데이터베이스 자원 사용률을 최적화하는 데 유용한 기능입니다. 이 가이드를 통해 암시적 연결 풀링의 개념을 이해하고, 실무에 적용하는 데 도움이 되기를 바랍니다.