OCI 세션 풀 API와 DRCP를 함께 사용

OCI 세션 풀 API와 DRCP 소개

Oracle Call Interface (OCI) 세션 풀 API는 Oracle 데이터베이스 연결을 효율적으로 관리하기 위한 강력한 도구입니다. 특히 Database Resident Connection Pooling (DRCP)과 함께 사용하면 애플리케이션의 확장성과 성능을 크게 향상시킬 수 있습니다. 이 글에서는 OCI 세션 풀 API와 DRCP를 연동하여 사용하는 방법에 대해 자세히 알아보고, 실제 코드 예제와 결과를 통해 설명하겠습니다.

OCI와 DRCP의 결합이 필요한 이유

  • 확장성 (Scalability): DRCP는 데이터베이스 서버에서 연결을 공유하여 서버 자원 사용률을 최적화합니다. OCI 세션 풀 API는 클라이언트 측에서 연결을 재사용하여 애플리케이션의 자원 소모를 줄입니다. 이 두 가지를 함께 사용하면 웹 애플리케이션처럼 많은 클라이언트 연결이 필요한 환경에서 뛰어난 확장성을 확보할 수 있습니다.
  • 자원 효율성 (Resource Efficiency): DRCP는 연결 풀을 관리하여 불필요한 연결 생성을 줄이고, OCI 세션 풀 API는 클라이언트 측에서 연결을 효과적으로 관리합니다. 결과적으로 데이터베이스 서버와 클라이언트 모두 자원을 효율적으로 사용할 수 있습니다.
  • 성능 향상 (Performance Improvement): 연결 재사용은 연결 설정 및 해제에 소요되는 오버헤드를 줄여 애플리케이션의 응답 시간을 단축시킵니다.

OCI 세션 풀 API 설정

OCI 세션 풀 API를 사용하려면 먼저 환경을 설정해야 합니다. 다음 단계를 따르세요:

  1. OCI 환경 초기화: OCIEnvCreate() 함수를 사용하여 OCI 환경을 초기화합니다.
  2. OCI 에러 핸들러 설정: OCIHandleAlloc() 함수를 사용하여 에러 핸들러를 할당하고, OCIErrorGet() 함수를 사용하여 에러 정보를 얻습니다.
  3. OCI 서비스 컨텍스트 생성: OCIHandleAlloc() 함수를 사용하여 서비스 컨텍스트를 생성합니다. 이 컨텍스트는 데이터베이스 연결에 사용됩니다.
  4. 사용자 인증 정보 설정: OCIAttrSet() 함수를 사용하여 사용자 이름과 비밀번호를 설정합니다.
  5. 데이터베이스 연결: OCISessionBegin() 함수를 사용하여 데이터베이스에 연결합니다.

DRCP 설정

DRCP를 활성화하려면 데이터베이스 서버에서 다음 단계를 수행해야 합니다:

  1. 연결 풀 시작: DBMS_CONNECTION_POOL.START_POOL 프로시저를 사용하여 연결 풀을 시작합니다. 예를 들어, 다음 코드를 사용하여 연결 풀을 시작할 수 있습니다.
     EXEC DBMS_CONNECTION_POOL.START_POOL(pool_name => 'MYPOOL', minsz => 10, maxsz => 100, incr => 5);
     
  2. 서비스 생성: DBMS_SERVICE.CREATE_SERVICE 프로시저를 사용하여 서비스를 생성하고, 이 서비스를 연결 풀과 연결합니다. 예를 들어, 다음 코드를 사용하여 서비스를 생성할 수 있습니다.
     EXEC DBMS_SERVICE.CREATE_SERVICE(service_name => 'MYSVC', network_name => 'MYSVC');
     EXEC DBMS_SERVICE.START_SERVICE(service_name => 'MYSVC');
     EXEC DBMS_SERVICE.MODIFY_SERVICE(service_name => 'MYSVC', connection_pool => 'MYPOOL');
     

OCI 세션 풀과 DRCP 연동 예제

다음은 C 코드를 사용하여 OCI 세션 풀과 DRCP를 연동하는 예제입니다. 이 예제에서는 데이터베이스에 연결하고, 쿼리를 실행하며, 연결을 닫습니다.

 #include 
 #include 
 #include 
 #include 

 #define UNUSED(x) (void)(x)

 int main(int argc, char *argv[]) {
 UNUSED(argc);
 UNUSED(argv);

 OCIEnv *envhp = NULL;
 OCIError *errhp = NULL;
 OCISvcCtx *svchp = NULL;
 OCISession *sesshp = NULL;
 OCIServer *srvhp = NULL;
 OCIDefine *defnp = NULL;
 OCIStmt *stmthp = NULL;

 OCIBind *bnd1p = NULL;

 text *user = (text *) "hr";
 text *pass = (text *) "hr";
 text *dbstr = (text *) "mysvc"; // DRCP 서비스 이름 사용
 text *stmtstr = (text *) "SELECT employee_id, last_name FROM employees WHERE department_id = 30";

 sword status = OCI_SUCCESS;
 sb4 emp_id;
 text last_name[30];
 sb4 rows_fetched = 0;

 // OCI 환경 초기화
 status = OCIEnvCreate(&envhp, OCI_DEFAULT, NULL, NULL, NULL, NULL, 0, NULL);
 if (status != OCI_SUCCESS) {
 printf("OCIEnvCreate failed\n");
 return 1;
 }

 // 에러 핸들 생성
 status = OCIHandleAlloc(envhp, (void**)&errhp, OCI_HTYPE_ERROR, 0, NULL);
 if (status != OCI_SUCCESS) {
 printf("OCIHandleAlloc (error) failed\n");
 return 1;
 }

 // 서버 핸들 생성
 status = OCIHandleAlloc(envhp, (void**)&srvhp, OCI_HTYPE_SERVER, 0, NULL);
 if (status != OCI_SUCCESS) {
 printf("OCIHandleAlloc (server) failed\n");
 return 1;
 }

 // 서비스 컨텍스트 생성
 status = OCIHandleAlloc(envhp, (void**)&svchp, OCI_HTYPE_SVCCTX, 0, NULL);
 if (status != OCI_SUCCESS) {
 printf("OCIHandleAlloc (svcctx) failed\n");
 return 1;
 }

 // 서버에 연결
 status = OCIServerAttach(srvhp, errhp, (text*)dbstr, strlen((char*)dbstr), OCI_DEFAULT);
 if (status != OCI_SUCCESS) {
 printf("OCIServerAttach failed\n");
 return 1;
 }

 // 서비스 컨텍스트 속성 설정: 서버 핸들 설정
 status = OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, srvhp, 0, OCI_ATTR_SERVER, errhp);
 if (status != OCI_SUCCESS) {
 printf("OCIAttrSet (server) failed\n");
 return 1;
 }

 // 세션 핸들 생성
 status = OCIHandleAlloc(envhp, (void**)&sesshp, OCI_HTYPE_SESSION, 0, NULL);
 if (status != OCI_SUCCESS) {
 printf("OCIHandleAlloc (session) failed\n");
 return 1;
 }

 // 사용자 이름 및 비밀번호 설정
 status = OCIAttrSet(sesshp, OCI_HTYPE_SESSION, (void*)user, (ub4)strlen((char*)user), OCI_ATTR_USERNAME, errhp);
 status = OCIAttrSet(sesshp, OCI_HTYPE_SESSION, (void*)pass, (ub4)strlen((char*)pass), OCI_ATTR_PASSWORD, errhp);
 if (status != OCI_SUCCESS) {
 printf("OCIAttrSet (user/pass) failed\n");
 return 1;
 }

 // 세션 시작
 status = OCISessionBegin(svchp, errhp, sesshp, OCI_CRED_RDBMS, OCI_DEFAULT);
 if (status != OCI_SUCCESS) {
 printf("OCISessionBegin failed\n");
 return 1;
 }

 // 서비스 컨텍스트에 세션 핸들 설정
 status = OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, sesshp, 0, OCI_ATTR_SESSION, errhp);
 if (status != OCI_SUCCESS) {
 printf("OCIAttrSet (session) failed\n");
 return 1;
 }

 // SQL 문장 핸들 생성
 status = OCIHandleAlloc(envhp, (void**)&stmthp, OCI_HTYPE_STMT, 0, NULL);
 if (status != OCI_SUCCESS) {
 printf("OCIHandleAlloc (stmt) failed\n");
 return 1;
 }

 // SQL 문장 준비
 status = OCIStmtPrepare2(stmthp, errhp, (text *)stmtstr, strlen((char *)stmtstr), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT);
 if (status != OCI_SUCCESS) {
 printf("OCIStmtPrepare2 failed\n");
 return 1;
 }

 // 결과 정의 (Define)
 status = OCIDefineByPos(stmthp, &defnp, errhp, 1, (dvoid*)&emp_id, sizeof(emp_id), SQLT_INT, NULL, NULL, NULL, OCI_DEFAULT);
 status = OCIDefineByPos(stmthp, &defnp, errhp, 2, (dvoid*)last_name, sizeof(last_name), SQLT_STR, NULL, NULL, NULL, OCI_DEFAULT);
 if (status != OCI_SUCCESS) {
 printf("OCIDefineByPos failed\n");
 return 1;
 }

 // SQL 문장 실행
 status = OCIStmtExecute(svchp, stmthp, errhp, 1, 0, NULL, NULL, OCI_DEFAULT);
 if (status != OCI_SUCCESS) {
 printf("OCIStmtExecute failed\n");
 return 1;
 }

 // 결과 가져오기
 while ((status = OCIStmtFetch2(stmthp, errhp, 1, OCI_FETCH_NEXT, 0, NULL, NULL, OCI_DEFAULT)) == OCI_SUCCESS) {
 rows_fetched++;
 printf("Employee ID: %d, Last Name: %s\n", emp_id, last_name);
 }

 printf("Total rows fetched: %d\n", rows_fetched);

 // 세션 종료
 status = OCISessionEnd(svchp, errhp, sesshp);
 if (status != OCI_SUCCESS) {
 printf("OCISessionEnd failed\n");
 return 1;
 }

 // 서버 연결 해제
 status = OCIServerDetach(srvhp, errhp, OCI_DEFAULT);
 if (status != OCI_SUCCESS) {
 printf("OCIServerDetach failed\n");
 return 1;
 }

 // 핸들 해제
 OCIHandleFree(envhp, OCI_HTYPE_ENV);
 OCIHandleFree(errhp, OCI_HTYPE_ERROR);
 OCIHandleFree(svchp, OCI_HTYPE_SVCCTX);
 OCIHandleFree(sesshp, OCI_HTYPE_SESSION);
 OCIHandleFree(srvhp, OCI_HTYPE_SERVER);
 OCIHandleFree(stmthp, OCI_HTYPE_STMT);

 return 0;
 }
 

코드 설명

  • text *dbstr = (text *) "mysvc";: DRCP 서비스 이름을 지정합니다. OCI 연결 시 이 서비스 이름을 사용하면 DRCP 연결 풀을 통해 데이터베이스에 연결됩니다.
  • OCISessionBegin(svchp, errhp, sesshp, OCI_CRED_RDBMS, OCI_DEFAULT);: 데이터베이스에 연결하는 함수입니다. DRCP를 사용하면 OCI는 자동으로 연결 풀에서 연결을 가져오거나, 필요한 경우 새 연결을 생성합니다.
  • OCIStmtExecute(svchp, stmthp, errhp, 1, 0, NULL, NULL, OCI_DEFAULT);: SQL 문장을 실행하는 함수입니다. DRCP 연결 풀을 통해 효율적인 쿼리 처리가 가능합니다.
  • OCISessionEnd(svchp, errhp, sesshp);: 세션을 종료하고 연결을 풀에 반환합니다. 연결이 더 이상 필요하지 않을 때 이 함수를 호출하여 연결을 풀에 반환해야 합니다.

실행 결과

위 코드를 컴파일하고 실행하면 다음과 같은 결과가 나타납니다.

 Employee ID: 103, Last Name: Ernst
 Employee ID: 118, Last Name: Himuro
 Employee ID: 122, Last Name: Khoo
 Employee ID: 178, Last Name: Grant
 Employee ID: 197, Last Name: Sciarra
 Total rows fetched: 5
 

OCI와 DRCP 연동 시 고려 사항

OCI와 DRCP를 함께 사용할 때 다음과 같은 사항을 고려해야 합니다:

  • 연결 풀 크기: DRCP 연결 풀의 크기를 적절히 설정해야 합니다. 너무 작으면 연결 요청이 지연될 수 있고, 너무 크면 서버 자원을 낭비할 수 있습니다.
  • 세션 관리: OCI 세션을 올바르게 시작하고 종료해야 합니다. 세션을 닫지 않으면 연결이 풀에 반환되지 않아 자원 누수가 발생할 수 있습니다.
  • 에러 처리: OCI 함수 호출 시 발생할 수 있는 에러를 적절히 처리해야 합니다. 에러 정보를 통해 문제 원인을 파악하고, 필요한 조치를 취할 수 있습니다.
  • 서비스 이름 구성: DRCP를 사용하기 위해 데이터베이스 연결 문자열에 DRCP에서 생성한 서비스 이름을 명시적으로 설정해야 합니다.

결론

OCI 세션 풀 API와 DRCP를 함께 사용하면 Oracle 데이터베이스 애플리케이션의 확장성, 자원 효율성, 성능을 크게 향상시킬 수 있습니다. 이 글에서 설명한 예제와 고려 사항을 통해 실제 애플리케이션 개발에 적용해 보세요. 또한, 애플리케이션의 특성과 요구 사항에 맞게 연결 풀 크기, 세션 관리 방법, 에러 처리 전략 등을 조정하여 최적의 성능을 얻을 수 있습니다.

위로 스크롤