문제
too many connections ,,,
max_connections 에러로 rds 접속이 안 되었던 문제가 있었다.
원인
처음엔 연결조차 안 됐었고 간신히 접속되었었다. SHOW PROCESSLIST; 해보니 58 rows 검색..
SHOW VARIABLES LIKE 'max_connections';
- 일단 이걸로 맥스 커넥션 수를 확인해보자
프리티어는 야박했다.
그래서 처음엔 max_connections 값을 늘리려고 했으나..
- 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개의 요청이 있다면 초당 몇 개의 요청을 받을 수 있을까?
일단 몇 가지 요소를 고려해야 함.
- 데이터베이스 연결 시간 : 데이터베이스 연결을 설정하고 사용하는 데 걸리는 시간.
- 프로세스 처리 시간 : 각 요청을 처리하는 데 걸리는 시간.
- 동시성 : 애플리케이션이 처리할 수 있는 동시 요청 수, 연결 풀 크기에 영향을 받음.
- 요청 복잡성 : 수행되는 데이터베이스 쿼리의 작업 복잡성과 특성.
추정 프로세스
- 요청당 평균 시간 측정
- 단일 요청이 완료되는 데 걸리는 평균 시간. → T
- 풀에서 연결을 가져오고, 쿼리 실행, 결과 처리, 연결을 풀로 반환하는 데 걸리는 시간 포함.
- ms 로 가정함
- 연결당 초당 요청 수 계산
- 하나의 연결이 밀리초(ms) 단위로 요청을 처리할 수 있다면, 약 1초에 1000 / T 개의 요청 처리 가능.
- 초당 요청 수
- 풀에 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 의 두배..
찾아보니 스레드의 스택 크기라고 한다.
내 로컬 값이 정상이고 RDS 설정값은.. AWS 측이 컴퓨팅 리소스에 맞게 반으로 줄여주셨나보다 ^^;허허…
우리 서버가 엄청 크고 대단한 쿼리 작업을 하거나 하진 않지만
추후 문제될 일이 있을 수 있으니 작은 값을 가지고 있다고 생각하고 염두는 해두어야겠다..
'개발 잡담' 카테고리의 다른 글
ECS 적용기 : EC2 와 Fargate (Serverless) 선택과 적용할 아키텍처 (0) | 2024.08.26 |
---|---|
Multi Stage Build 로 스프링 프로젝트 이미지 축소해보기 (0) | 2024.08.26 |
CI/CD ) Github Actions 로 CI/CD 해보기 (0) | 2024.07.18 |
Test 서버를 지키자 ! AWS 청구서 훑어보고 CDN 적용 건의하기 (0) | 2024.07.09 |
도메인 주도 개발 시작하기를 읽고 (1) | 2024.07.02 |