암시적 연결 풀링 (Implicit Connection Pooling)

개요

Oracle Database 23ai에서 도입된 암시적 연결 풀링(Implicit Connection Pooling, PROP)은 애플리케이션 개발자가 명시적인 연결 관리를 수행하지 않아도 데이터베이스 연결을 효율적으로 재사용할 수 있도록 지원하는 기능입니다. 이 기능은 특히 다중 티어 환경에서 데이터베이스 연결 수를 최소화하고 애플리케이션의 확장성과 성능을 향상시키는 데 유용합니다.

암시적 연결 풀링의 주요 개념

암시적 연결 풀링은 다음과 같은 주요 개념을 기반으로 합니다.

  • 연결 풀 (Connection Pool): 데이터베이스 연결을 미리 생성하여 보관하고, 필요할 때 애플리케이션에 제공하는 메커니즘입니다.
  • 연결 브로커 (Connection Broker): 풀링된 서버들을 관리하고 연결 핸드오프 프로세스를 처리하는 컴포넌트입니다.
  • 세션 퓨리티 (Session Purity): 애플리케이션이 재사용되는 연결에서 이전 세션의 상태가 유지되지 않도록 보장하는 기능입니다.
  • 연결 클래스 (Connection Class): OCI 애플리케이션에서 연결 유형을 식별하기 위해 사용되는 논리적 이름입니다.

암시적 연결 풀링의 장점

암시적 연결 풀링은 다음과 같은 다양한 장점을 제공합니다.

  • 확장성 향상: 데이터베이스 연결 관리를 자동화하여 더 많은 동시 사용자 및 트랜잭션을 지원할 수 있습니다.
  • 자원 효율성 증대: 데이터베이스 연결을 재사용하여 서버 자원 사용률을 최적화합니다.
  • 개발 편의성 증진: 애플리케이션 개발자가 연결 관리에 대한 부담을 덜 수 있습니다.
  • 클라우드 환경 최적화: 클라우드 환경에서 데이터베이스 연결 관리를 효율적으로 수행하여 비용을 절감할 수 있습니다.

암시적 연결 풀링 구성

암시적 연결 풀링은 데이터베이스 서버와 OCI 클라이언트 측에서 모두 구성해야 합니다.

데이터베이스 서버 구성

  1. Listener 설정 확인: 리스너가 OCI 연결을 허용하도록 구성되어 있는지 확인합니다.
  2. DRCP 활성화 확인: DRCP (Database Resident Connection Pooling)가 활성화되어 있는지 확인합니다. DRCP는 암시적 연결 풀링을 지원하기 위한 핵심 기능입니다.

OCI 클라이언트 구성

  1. OCI 라이브러리 연결: OCI 환경이 스레드 라이브러리와 연결되어 있는지 확인합니다.
  2. 연결 풀링 활성화: 연결 문자열 또는 구성 파일에서 연결 풀링을 활성화합니다.
  3. 연결 클래스 정의: 연결 유형을 식별하기 위한 연결 클래스를 정의합니다.
  4. 세션 퓨리티 설정: 세션 퓨리티를 활성화하여 연결 재사용 시 이전 세션의 상태가 유지되지 않도록 합니다.

예제 코드 및 결과

다음은 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의 암시적 연결 풀링은 애플리케이션 성능을 향상시키고 데이터베이스 자원 사용률을 최적화하는 데 유용한 기능입니다. 이 가이드를 통해 암시적 연결 풀링의 개념을 이해하고, 실무에 적용하는 데 도움이 되기를 바랍니다.

위로 스크롤