Jun 13 2008

DB 연결의 과부하를 막기 위해 CRecordSet 을 지양하자

분류: TroubleShooting 태그: ,, , , Heart @ 12:15 오전

Trackback : http://dev.heartsavior.net/archives/145/trackback/

최근 들어 DB 액세스가 잦은 데몬 프로그램을 자주 개발하고 있다.
보통은 모니터링/배치 의 성향을 가지고 있어서 단시간에 DB 액세스가 많이 발생하는 프로그램들이다. CRUD 도 하고, BLOB/CLOB 데이터를 가지고 작업도 하기도 한다.

근데 실 데이터를 가지고 적용하다 보면 로직상 별 문제가 없을 것 같은 프로그램들이 소켓 연결 / DB 연결 등에서 오류가 발생하는 경우를 자주 접하게 되었다.

며칠 전에도 다른 회사 솔루션과 연동하는 프로그램에서 DB 연결 문제가 가끔 발생하는 문제로 골머리를 앓고 있었는데, 로직 자체에는 별 문제가 없었다. DB 연결 이후 해제를 하지 않는 부분이 있는지 체크했지만 그런 부분이 있지는 않았다.

DB 연결이 폭주한 것은 아닐까 싶어 netstat -na 를 실행했는데, 뭔가 이상한 점을 발견했다.
ESTABLISHED 상태는 정상적인 갯수인데, TIME_WAIT 상태가 실시간으로 늘어나는 것이었다.

해당 부분을 찾아나선 결과, DB에서 BLOB 내용을 가져오기 위해 CRecordSet을 사용하고 있었다.
CRecordSet 은 레코드셋을 열 때 DB에 연결을 하고, 작업을 한 다음, 레코드셋을 닫을 때 DB의 연결을 끊는다.
그러므로 BLOB 데이터 한 번 가져올 때 DB 연결/해제가 한 번씩 일어나서 단시간에 DB 리소스를 좀먹는 현상이 일어난 것이다.(TIME_WAIT가 쌓이는 것으로 보면 연결 해제를 놓친 건 아니었다.)
Connection Pool로 DB 연결을 관리하고 있는 상황이 무색한 상황이었다.

테스트할 때 문제를 발견하지 못한 게, DB도 다른 PC에 있었고, 수행 PC의 사양도 그다지 높지 않아 순간적으로 DB 연결/해제가 많이 일어나지는 않아서 문제를 발견하지 못했는데, 실 적용 서버는 DB도 로컬이고 서버의 사양이 무려 XEON 쿼드 3.2G * 4개 / 8G 메모리이다 보니 미칠듯한 속도로 처리하면서 TIME_WAIT가 미칠듯한 속도로 쌓인 것 같다.

CRecordSet을 들어내고 Connection Pool에서 관리하는 Connection에서 BLOB 데이터를 직접 처리하도록 변경하면서 문제가 해결됐다.
(확정짓기엔 며칠 더 지켜봐야 될 것 같긴 하지만… netstat 결과가 희망적이니 별 문제 없을 것 같다)

“연결 하면 해제를 해야 한다” 는 기초적인 사항을 지키는 것은 말할 것도 없고, “소켓 연결은 최소한으로 하고 한 번 연결되었을 때 최대한의 처리를 하자” 는 사항을 자주 체크해 봐야겠다는 교훈을 얻었다.
(Connection Pool의 목적이기도 한데, Connection Pool을 사용하면서도 그냥 라이브러리 마냥 사용하다 보니 원리원칙을 잠시 잊었던 것 같다. 좀 더 꼼꼼해지자.)