성능 개선 방법론과 조인·애플리케이션·서버 병목
성능 문제를 측정→병목 식별→가설→변경→재측정의 순서로 개선합니다. 실행계획과 통계, NL·Hash·Sort Merge 조인의 선택 조건, 애플리케이션 왕복·배치 문제, CPU·메모리·I/O·동시성 병목을 한 개요에서 구분합니다.
핵심 요약
성능 개선은 “인덱스를 추가한다” 또는 “서버를 증설한다”가 아니라 목표와 기준선을 정하고 실제 병목을 측정한 뒤 한정된 변경을 적용하고 다시 측정하는 검증 과정이다. SQL, 조인, 애플리케이션, DB 인스턴스, OS·스토리지·네트워크는 서로 영향을 주므로 증상과 원인을 구분해야 한다.
증상 정의 → 기준선 수집 → 병목 계층 식별 → 원인 가설
→ 변경안·부작용 검토 → 시험 → 재측정 → 회귀 감시
학습 목표
- 성능 개선 절차와 핵심 측정값을 설명합니다.
- 예상 실행계획과 실제 실행 통계, 통계정보·카디널리티의 관계를 구분합니다.
- NL·Hash·Sort Merge 조인의 일반적 선택 조건을 판별합니다.
- 애플리케이션과 서버·인스턴스 병목을 지표 기반으로 분리합니다.
1. 개념 설명
1.1 성능 목표와 기준선
성능은 단일 평균 응답시간이 아니라 업무별 목표로 정의한다.
- 응답시간: 평균뿐 아니라 95/99백분위와 최대 지연
- 처리량: 초당 트랜잭션·쿼리·배치 건수
- 자원: CPU 사용률/런큐, 논리·물리 읽기, I/O 지연, 메모리·임시공간, 네트워크 왕복
- 동시성: 잠금 대기, 데드락, 세션/연결 풀 대기, 로그 동기화 대기
- 효율: 요청당 SQL 수, 실행당 처리 행, 읽은 행 대비 반환 행, 계획 추정 대비 실제 행
기준선은 정상 시간대와 문제 시간대를 동일한 업무량·데이터 규모·환경에서 비교할 수 있어야 한다.
1.2 SQL 처리와 실행계획
비용 기반 최적화기는 통계정보를 사용해 접근 경로, 조인 순서, 조인 방법을 선택한다. 핵심 점검은 다음과 같다.
- 조건의 선택도와 예상/실제 카디널리티 차이
- 전체 스캔·인덱스 접근이 읽은 블록과 반환 행에 적합한지
- 조인 입력 순서와 중간 결과 크기
- 정렬·해시가 메모리를 넘어서 임시공간으로 유출되는지
- 반복 실행 횟수, 바인드 값 편향, 통계 최신성
EXPLAIN으로 얻은 예상 계획은 실제 실행 환경·바인드·통계와 다를 수 있다. 가능한 경우 실제 행 수, 반복 횟수, 시간, I/O를 포함한 실행 통계를 확인한다.
1.3 조인 방법
| 조인 | 일반적 동작 | 유리한 경향 | 주의점 |
|---|---|---|---|
| Nested Loops | 외부 행마다 내부 입력을 탐색 | 외부 결과가 작고 내부에 효율적 접근 경로가 있음, 첫 행 응답 | 외부 행이 커지면 내부 반복 접근 폭증 |
| Hash Join | 한 입력으로 해시 구조를 만들고 다른 입력을 탐색 | 큰 집합의 동등 조인, 충분한 메모리 | 비동등 조인 제약, 해시 유출·큰 build 입력 |
| Sort Merge Join | 양쪽을 조인 키 순으로 정렬/스캔해 병합 | 이미 정렬됨, 큰 집합, 범위·비동등 조건에서 고려 | 정렬 비용·임시공간, 첫 행 지연 |
정확한 선택은 DBMS, 통계, 메모리, 병렬성, 조인 종류에 따라 달라진다. “큰 테이블=Hash”, “인덱스=NL” 같은 단일 규칙으로 결정하지 않는다.
1.4 애플리케이션 병목
- N+1 SQL과 행 단위 반복 처리
- 과도한 Auto-commit과 짧은 DML의 개별 네트워크 왕복
- 연결 풀 부족 또는 과대 설정
- 불필요한 열·행 조회, 큰 객체의 조기 로딩
- 비효율적 페이지네이션, 너무 작은 fetch 크기
- 긴 트랜잭션, 외부 API 호출을 포함한 DB 트랜잭션
- 동일 SQL의 문자열 조합으로 파싱·계획 재사용 저하
애플리케이션 로그·트레이스에서 요청당 SQL 수, 각 SQL 시간, 대기, 전송량을 연결해야 한다.
1.5 서버·인스턴스 병목
| 자원·대기 | 관찰 징후 | 가능한 원인 예시 |
|---|---|---|
| CPU | 높은 사용률, 런큐, CPU time 증가 | 비효율 SQL, 과도한 파싱, 압축·암호화, 동시성 과다 |
| 메모리·버퍼 | 물리 읽기 증가, 캐시 미스, swap | 작업집합 초과, 대형 정렬·해시, 메모리 설정 |
| 스토리지 I/O | 읽기/쓰기 지연, queue 증가 | 랜덤 I/O 폭증, checkpoint, temp spill, 로그 병목 |
| 로그·커밋 | commit 대기, 로그 flush 지연 | 잦은 소규모 커밋, 느린 로그 장치 |
| 잠금·동시성 | lock wait, deadlock, 긴 세션 | 긴 트랜잭션, 갱신 순서 불일치, 핫 행 |
| 네트워크 | RTT 증가, 전송 대기 | 원격 DB, 큰 결과, 다수 왕복 |
자원 사용률이 높다는 사실만으로 병목이라고 단정하지 않는다. 업무 처리량이 목표를 만족하고 대기·지연이 없다면 높은 사용률은 효율적 사용일 수 있다.
2. 구성요소와 관계
| 증상 | 먼저 볼 계층 | 추가 확인 |
|---|---|---|
| 특정 SQL만 느림 | 실제 계획·카디널리티·I/O | 바인드 값, 통계, 잠금 대기 |
| 모든 API가 동시에 느림 | DB·OS 대기, 연결 풀 | CPU, I/O, 로그, 네트워크 |
| 첫 화면만 느림 | 조인·정렬·초기 연결 | first-row 목표, 캐시 warm-up |
| 배치 시간이 선형 이상 증가 | 중간 결과·temp·커밋 | 데이터 증가율, 정렬/해시 spill |
| DB CPU는 낮고 API는 느림 | 애플리케이션/네트워크 | N+1, 외부 호출, 풀 대기 |
3. 성능 개선 절차
- 문제와 목표 정의: 어떤 업무가 어느 부하에서 몇 초/건을 만족해야 하는지 정한다.
- 재현과 기준선 수집: SQL, 계획, 대기, 자원, 애플리케이션 호출을 같은 시간축으로 수집한다.
- 병목 계층 결정: DB CPU, I/O, 잠금, 네트워크, 애플리케이션 대기 중 지배 요소를 찾는다.
- 원인 가설 수립: 카디널리티 오차, 반복 접근, temp spill, 긴 트랜잭션 등 검증 가능한 가설로 쓴다.
- 변경안 평가: SQL·통계·인덱스·데이터 액세스·설정·자원 변경의 효과와 쓰기·운영 부작용을 비교한다.
- 통제된 시험과 재측정: 가능한 한 한 번에 주요 변수 하나를 바꾸고 동일 부하로 비교한다.
- 회귀 감시: 다른 SQL·배치·쓰기 비용, 계획 변동과 장기 추세를 확인한다.
4. 사례와 적용
사례: 고객 100명과 최근 주문 조회
애플리케이션이 고객 목록을 1회 조회하고 고객마다 주문 SQL을 실행해 총 101회 왕복한다. 각 SQL은 5ms이지만 네트워크 왕복과 직렬 실행 때문에 전체가 1초를 넘는다. DB CPU는 낮다.
판단: 서버 CPU 증설보다 N+1 제거가 우선이다. 고객별 필요한 주문을 한 번의 집합 처리 또는 제한된 배치 조회로 가져오고, 결과 중복·메모리·페이지 요구를 검증한다.
다른 사례로, 실제 계획에서 NL 조인의 외부 결과가 추정 10행·실제 500,000행이라면 내부 인덱스 접근이 500,000회 반복될 수 있다. 통계·조건 상관관계·조인 순서와 Hash 조인 후보를 함께 검증한다.
5. 비교와 구분
| 구분 | 증상 처방 | 근거 중심 개선 |
|---|---|---|
| 인덱스 | 느리면 인덱스 추가 | 읽기 감소와 쓰기·공간·유지비를 실행 통계로 비교 |
| 힌트 | 계획이 마음에 안 들면 고정 | 통계·카디널리티 원인을 먼저 확인하고 필요 시 제한적으로 사용 |
| 메모리 | temp 사용이면 무조건 증설 | 작업 크기, 동시성, spill 원인과 전체 메모리 한도 검증 |
| 연결 | 풀 대기면 연결 수 증가 | 긴 SQL·트랜잭션과 DB 처리 한계를 먼저 확인 |
| 서버 | CPU가 높으면 scale-up | 처리량·대기·SQL별 CPU를 확인해 원인 제거와 증설을 비교 |
시험 판단 포인트
- 최적화기는 접근 경로뿐 아니라 조인 순서와 조인 방법을 함께 선택한다.
- NL은 외부 행 수와 내부 접근 비용의 곱이 핵심이고, Hash는 build 입력 크기와 메모리, Sort Merge는 정렬·정렬상태가 중요하다.
- 예상 실행계획과 실제 실행 결과는 다를 수 있으므로 실제 행 수와 반복 횟수를 확인한다.
- 애플리케이션 왕복·N+1은 DB CPU가 낮아도 전체 응답을 느리게 할 수 있다.
- 개선 후에는 목표 SQL만 아니라 쓰기 비용, 다른 SQL, 동시 부하와 운영성을 재검증한다.
자주 틀리는 부분
- 평균 응답시간만 보고 긴 꼬리 지연과 처리량을 무시한다.
- 실행계획의 비용 숫자를 서로 다른 SQL·환경의 절대 시간처럼 비교한다.
- 인덱스 추가가 항상 I/O를 줄이며 DML 비용은 없다고 생각한다.
- DB가 느리다는 사용자 증상만으로 DB 서버가 원인이라고 단정한다.
- 한 번에 SQL, 설정, 인덱스, 서버 사양을 모두 바꿔 어떤 변경이 효과를 냈는지 알 수 없게 한다.
개념 확인 문제
문제를 누르면 바로 아래에서 정답과 해설을 확인할 수 있습니다.
01[객관식] 근거 중심 성능 개선의 올바른 순서는? ① 인덱스 추가→증상 정의→측정 ② 측정→병목 식별→가설→변경→재측정 ③ 서버 증설→힌트 적용→통계 삭제 ④ 실행계획 고정→업무 목표 정의
정답: ②
- ① 원인을 확인하지 않은 인덱스 추가는 부작용과 오진 위험이 있다.
- ② 목표·측정·가설·변경·검증의 순서가 근거 중심 개선이다.
- ③ 통계 삭제나 무조건 증설은 원인 확인 절차가 아니다.
- ④ 업무 목표와 기준선 없이 계획만 고정하면 장기 회귀를 판단하기 어렵다.
02[객관식] NL 조인이 유리할 가능성이 가장 높은 상황은? ① 외부 입력이 작고 내부 조인 키에 효율적인 접근 경로가 있다. ② 양쪽 모두 매우 크고 동등 조인이며 내부 접근을 수백만 번 반복한다. ③ 모든 행을 정렬해야만 하는 범위 조인이다. ④ 조인 조건이 없다.
정답: ①
- ① 작은 외부 결과와 저비용 내부 탐색은 NL의 반복 비용을 제한한다.
- ② 큰 동등 조인에서는 Hash 등 다른 방법을 검토할 가능성이 높다.
- ③ 정렬된 큰 집합이나 범위 조건은 Sort Merge 후보가 될 수 있다.
- ④ 조인 조건이 없으면 카테시안 결과 위험이 있으며 조인 방법 선택의 정상 사례가 아니다.
03[연결형] N+1, Hash spill, lock wait를 각각 애플리케이션 왕복, 메모리/임시공간, 동시성 병목과 연결하세요.
정답: N+1→애플리케이션 왕복, Hash spill→메모리/임시공간, lock wait→동시성 병목.
- 하나의 사용자 지연에 여러 병목이 겹칠 수 있으므로 동일 시간축의 지표를 함께 본다.
04[계획 판독] NL 외부 입력이 추정 10행, 실제 500,000행이고 내부 인덱스 탐색이 외부 행마다 실행된다. 느린 이유와 확인·개선 항목을 설명하세요.
모범 답안:
- 최적화기가 외부 행을 10개로 예상해 NL을 선택했지만 실제 500,000개여서 내부 인덱스 탐색이 대량 반복되는 것이 핵심 원인이다.
- 조건 선택도, 통계 최신성, 열 상관관계, 바인드 값 편향, 조인 순서를 확인한다.
- 통계·SQL 조건 개선, 외부 입력 축소, Hash 조인 후보 또는 적절한 집합 처리로 바꾼 뒤 실제 I/O·시간·다른 업무 영향을 재측정한다.
05[사례 판단] API 응답은 느리지만 DB CPU와 I/O는 낮고 연결 풀 대기시간이 길다. 우선 수집할 지표 세 가지와 개선 순서를 제시하세요.
모범 답안:
- 지표: 요청당 연결 획득 대기시간, 풀 사용/대기/최대 연결 수, 연결을 보유한 트랜잭션·SQL 시간. 요청당 SQL 수와 외부 API 시간도 추가한다.
- 순서: 긴 트랜잭션·누수·느린 SQL을 먼저 제거하고, 애플리케이션 동시성과 DB 처리 한계를 확인한 뒤 풀 크기·타임아웃을 조정한다.
- 단순히 풀을 키우면 DB 동시 부하와 잠금 경합이 커질 수 있다.