본문 바로가기
개발 잡담

DB ) Too many Connections 문제.. (부제:커넥션 하나 당 메모리를 얼마나 사용할까?)

by 휴일이 2024. 8. 6.

문제

too many connections ,,,

max_connections 에러로 rds 접속이 안 되었던 문제가 있었다.

원인

처음엔 연결조차 안 됐었고 간신히 접속되었었다. SHOW PROCESSLIST; 해보니 58 rows 검색..

SHOW VARIABLES LIKE 'max_connections';
  • 일단 이걸로 맥스 커넥션 수를 확인해보자

 

프리티어는 야박했다.

그래서 처음엔 max_connections 값을 늘리려고 했으나..

 

출처 : AWS Documentation

 

  • AWS 가 알아서 인스턴스에 맞게 설정 값을 조절해준다.
  • 너네 리소스에 맞게 알아서 맞춰줬으니까 맘대로 늘리면 곤란해질 수도 있다고…한단다.

 

SHOW PROCESSLIST;

커넥션으로 연결되어있는 친구들을 확인.

 

 

  • 모르는 IP들이 눈에 띄었다. (개발팀 계정에서 사용하는 IP 들이 아니었다.)
  • 개발팀에 수소문해보니
    • 동기님이 예전에 Cloud watch 연동해보시면서 개인 계정으로 사용했던 인스턴스 였다.
    • 앱이 아직 열려있어서 커넥션이 계속 유지되고 있었던 것…

 

1차 해결 완료

열려있는 앱을 닫아달라고 요청했다. (…)

SHOW PROCESSLIST;

 

 

 

  • 외부 앱 연결이 끊기면서 커넥션 연결이 -10 되었다.
  • 당장 급한 불 끄기

결과가 37rows

 

 

 

분석

스프링에서 HikariCp 가 기본으로 커넥션 풀에 10개의 커넥션을 풀링해준다. (직접 관리해준다.) 이것은 물론 연결을 맺고 끊는 비용이 비싸기 때문이다.

현재 커넥션 유지 비용

SHOW VARIABLES LIKE 'sort_buffer_size';
SHOW VARIABLES LIKE 'join_buffer_size';
SHOW VARIABLES LIKE 'read_buffer_size';
SHOW VARIABLES LIKE 'read_rnd_buffer_size';
SHOW VARIABLES LIKE 'thread_stack';
SHOW VARIABLES LIKE 'net_buffer_length';

### 각각 결과
262144 262144 262144 524288 262144 16384

sort_buffer_size = 262144 / 1024 = 256KB
join_buffer_size = 262144 / 1024 = 256KB
read_buffer_size = 262144 / 1024 = 256KB
read_rnd_buffer_size = 262144 / 1024 = 256KB
thread_stack = 524288 / 1024 = 512KB
net_buffer_length = 16384 / 1024 = 16KB

256KB + 256KB + 256KB + 256KB + 512KB + 16KB = 1552KB
  • 커넥션 유지 비용 : 한 개당 약 1.8MB

현재 우리 RDS

  • db.t2.micro
  • 램 1GiB

커넥션 오버헤드 따지다가 DB 에서 OOM 나는 경우도 있다고 하는데 개당 1.8MB 정도면..아주 위험한 정도는 아닌 것 같다.

다만 다른 프레임워크는 기본적으로 커넥션 풀링을 안 해주는 경우도 있다.

  • Spring Boot 도 엄밀히 얘기하면 Jpa Dependency 가 HikariCp 를 채택하여 자동으로 풀링해오도록 설정된 것.
  • 사실 현재 커넥션 풀 설정은 우리 DB 스펙 db.t2.micro 에 맞지 않게 과하게 열어놓은 것이 맞다.
    • 현재 : 커넥션 풀에 기본 10개 유지..

 

커넥션 풀에 10개의 요청이 있다면 초당 몇 개의 요청을 받을 수 있을까?

일단 몇 가지 요소를 고려해야 함.

  1. 데이터베이스 연결 시간 : 데이터베이스 연결을 설정하고 사용하는 데 걸리는 시간.
  2. 프로세스 처리 시간 : 각 요청을 처리하는 데 걸리는 시간.
  3. 동시성 : 애플리케이션이 처리할 수 있는 동시 요청 수, 연결 풀 크기에 영향을 받음.
  4. 요청 복잡성 : 수행되는 데이터베이스 쿼리의 작업 복잡성과 특성.

추정 프로세스

  1. 요청당 평균 시간 측정
  • 단일 요청이 완료되는 데 걸리는 평균 시간. → T
    • 풀에서 연결을 가져오고, 쿼리 실행, 결과 처리, 연결을 풀로 반환하는 데 걸리는 시간 포함.
    • ms 로 가정함
  1. 연결당 초당 요청 수 계산
  • 하나의 연결이 밀리초(ms) 단위로 요청을 처리할 수 있다면, 약 1초에 1000 / T 개의 요청 처리 가능.
  1. 초당 요청 수
  • 풀에 10 개의 연결이 있으면 초당 요청 수 → 10 * (1000 / T)

계산 예시

요청 ( T ) 에 대한 평균 시간이 100ms 라면…

  • 연결당 초당 요청 수 : 1000 / 100 = 10
  • 10 개의 연결 : 10 * 10 = 100 request per second

고려해야할 요소

  • 쿼리 복잡성 : 당연히 간단한 쿼리는 짧게 걸리지만 여러 개의 조인이나 대규모 데이터 세트가 포함된 복잡한 쿼리는 오래 걸림.
  • 캐싱 : 캐시를 사용하면 DB 부하를 크게 줄이고 처리량 늘릴 수 있음.
  • 부하 테스트 : JMeter , Gatling 과 같은 도구를 사용하면 부하 테스트를 수행하여 특정 애플리케이션에 대한 정확한 측정값을 얻을 수 있다고 → GPT 가 그러네요

실제 처리량은

  • 네트워크 지연, 하드웨어 사양, 데이터베이스 성능, 애플리케이션 등 여러 요인에 따라 다름
  • 프로덕션 환경을 최대한 모방하여 현실적인 부하 테스트를 할 수 있어야 함.

 

그래서 2줄 요약 좀..

조건 : 기본 연결 풀 크기 10, 평균 요청 처리시간 100ms
→ 초당 약 100개의 요청 처리 가능단순화된 추정치이며 실제 성능은 부하 테스트를 통해 검증해야 함.

 

 

2차 해결법 제안

  • 기존 앱들 기본 커넥션 연결 수 제한
    • 테스트 서버 ~1개
    • 기타 운영 서버 앱 ~3개

현재 동시 접속자가 많지 않아 커넥션을 10개까지 유지하는 것은 과한 설정이 아닌가 하는 생각이 든다.

운영서버는 기존에 1/3 정도로 줄이고 테스트 서버는 최대 1개로 유지하는 것이 어떨까?

어차피 “최대 커넥션 풀” 값이 아니라 “기본 유지 값”이라서 문제 없을 것이라고 생각한다.

- 이번 주간 회의 때 건의해보고 상황을 볼 것...

 

 

참고로 집에서 해보았을 땐

262144 262144 131072 262144 1048576 16384
256KB + 256KB + 128KB + 256KB + 1024KB + 16KB = 1936KB
  • 약 1.9MB 가 나왔당.
  • 아무것도 안 건드린 설정인데 RDS 최소 설정보다 메모리를 많이 쓴다 오왕~

참고로 저 thread_stack 부분에 리소스 할당이 많이 되어있었다. RDS 의 두배..

 

출처 : MySQL Documentation

 

찾아보니 스레드의 스택 크기라고 한다.

내 로컬 값이 정상이고 RDS 설정값은.. AWS 측이 컴퓨팅 리소스에 맞게 반으로 줄여주셨나보다 ^^;허허…

 

우리 서버가 엄청 크고 대단한 쿼리 작업을 하거나 하진 않지만

추후 문제될 일이 있을 수 있으니 작은 값을 가지고 있다고 생각하고 염두는 해두어야겠다..

 

728x90