10장. (부록) 성능을 생각하자 - 성능 향상을 위해
성능 측정 지표
처리 시간 (Processing Time) 또는 응답 시간 (Response Time)
- 특정 처리의 시작부터 종료까지 걸린 시간
- 사용자에 대한 영향이 가시적
처리율 (Throughput)
- 단위 시간당 처리 가능한 트랜잭션. 시간 단위의 지표
- 초당 50건의 트랜잭션 처리 = 50 TPS(Transaction Per Second)
- 시스템의 자원 용량(Resource Capacity)을 결정하는 요인
- 필요한 자원의 양은 동시에 실행된 처리량에 비례함
정점과 한계점
병목 (Bottleneck)
- 한 가지 자원이라도 한계에 이른 시점부터 성능 저하 발생 ➡️ 병목 발생. 응답 시간 상승, 처리율 하락
- Bottleneck: 동시에 실행되는 처리가 많아지는 순간에 대비하여 자원을 준비해두지 않으면 Peak에서 극단적인 지연 발생
- Bottleneck Point: 최초로 한계에 이른 자원
- Breaking Point: 처리율과 응답시간이 극단적으로 나빠지기 시작하는 처리량
- Sizing 또는 Capacity Planning: 정점을 상정한 자원을 확보하는 것
주기형과 돌발형
- 업무 시스템과 같은 주기형의 경우 과거 실적을 조사하여 성장률을 추가하면 어느정도 액세스 집중도 파악 가능
- 온라인 상거래 사이트와 같은 돌발형의 경우 사용자가 정해져있지 않고 세일이나 이벤트에 따라 다르므로 액세스 집중 발생 예측이 어려움
- 정점이 아닌 경우와 정점인 경우에 필요한 자원량 차이가 크므로 자원 낭비 발생 가능
- 돌발형 액세스 집중에 대응하기 위해 클라우드(Cloud)를 통해 동적 자원 관리 가능
- 가상화를 기반으로 자원량을 유연하게 변동할 수 있는 기술. 물리 자원의 임대 모델
- Scale Up(고성능 장비 도입), Scale Out(장비 추가 도입)이 쉬움
데이터베이스와 병목
데이터베이스 병목 이유
- 취급하는 데이터양이 가장 많음
- 최근 데이터가 폭발적으로 증가
- 저장소 자원에서 병목이 많이 발생
- SQL문의 응답시간 증가, 데이터베이스 처리율 감소 등의 문제 발생
- 자원 증가를 통한 해결이 어려움
- 데이터베이스의 경우 동적 자원 관리가 어려움
- 데이터베이스의 병목 지점은 CPU나 메모리가 아닌 저장소. 즉, 하드디스크 ➡️ Scale Out이 어려움
- 주어진 자원 범위 내에서 융통성 있게 처리하기 위해 튜닝 기술 사용
- 튜닝: 애플리케이션을 효율화하여 같은 양의 자원이라도 성능을 향상하게 하는 기술
SQL 실행 과정
Parse ➡️ Execution Plan 작성 (통계 정보 사용) ➡️ Execution Plan 평가 ➡️ 데이터 액세스
Parse
- Parser 프로그램이 데이터베이스가 받은 SQL문에 문법적으로 잘못된 부분이 없는지 점검
- 구문 오류 발견 시 SQL문과 오류메세지를 사용자에게 반환
Execution Plan과 Optimizer
-
Parse 후 SQL문에 필요한 데이터에 어떤 경로로 접근할 지 플랜을 세움
-
Optimizer 프로그램이 통계정보를 참고하여 여러 가능한 플랜 중 어떤 플랜이 가장 효율적인지 계산하여 Execution Plan을 결정
💡 Optimizer란?
가장 효율적인 방법으로 SQL을 수행할 최적의 처리 경로를 생성해주는 DBMS의 핵심 엔진
네비게이션의 최단거리 찾기와 같음
Rule-Based Optimizer와 Cost-Based Optimizer가 있음
대부분 옵티마이저가 가장 효율적인 실행 계획을 알려주지만, SQL이 복잡한 경우 실수할 가능성이 큼
실제 운영자가 업무 특성을 고려하여 더 효율적인 액세스 경로를 찾아낼 수도 있음
이때 옵티마이저 힌트를 사용하여 데이터 액세스 경로 변경 가능
ex) SELECT /*+ INDEX(A 이름_PK) */
참고자료 : 친절한 SQL 튜닝, 조시형 지음
통계 정보(Statistics)
실행 계획 (Execution Plan)
실행 계획 확인하기
Full Scan과 Range Scan
Index
-
Range Scan을 위해서는 Index가 필요
-
적절한 Index가 없다면 Full Scan을 하여야 함
-
실행 계획에서 possible_keys
와 key
의 PRIMARY
는 Primary Key의 Index를 사용하였음을 나타냄
-
기본키 구성 열에는 반드시 인덱스가 생성됨
-
인덱스 목록 확인
mysql> show index from 테이블명;
- Primary Key인 id에 대한 인덱스와 Unique Key인 email에 대한 인덱스로 총 2개의 인덱스가 존재함
-
인덱스 생성하기
mysql> create index 인덱스명 on 테이블명(열명);
-
인덱스는 데이터베이스의 성능 향상 수단의 가장 일반적인 방법
- SQL문 변경 없이 성능 개선 가능
- 테이블의 데이터에 영향을 주지 않음
- 일정한 (때로는 극적인) 효과 기대 가능
-
인덱스를 적절히 생성하여 풀 스캔을 회피하고 일부 레코드만 스캔하여 성능을 향상 시킬 수 있음
- 인덱스를 적절히 생성해두면 인덱스 사용을 지시하지 않아도 옵티마이저가 인덱스를 사용하도록 설정함
- 옵티마이저가 인덱스 사용보다 풀 스캔이 더 빠르다고 판단하는 경우에는 인덱스를 사용하지 않음
Index 구조 B-tree
-
B-tree 인덱스 외에도 비트맵 인덱스, REVERSE KEY 인덱스 등이 존재하지만, 대부분 B-tree 인덱스가 사용됨
-
B-tree 인덱스의 구조
- 각각의 데이터는 Node
- 가장 상위의 노드는 Root Node, 가장 아래의 노드는 Leaf Node, 중간 노드는 Branch Node
- 리프 노드 아래의 숫자는 데이터가 포함된 테이블의 페이지 수
-
B-tree 인덱스는 반드시 데이터를 정렬된 상태로 유지
-
특정 데이터를 찾을 때 루트 노드에서 탐색을 시작하여 데이터의 대소를 비교하고 한 단계씩 아래로 진행해나가며 리프 노드에 도달
-
이진 탐색을 수행하는 균형 트리이므로 성능이 우수하며 데이터양이 커질수록 성능 개선 효과가 큼
- 균형 트리(Balanced-tree): 루트로부터 리프까지의 거리가 일정
- 다만 처음 B-tree 생성 시에는 균형 트리지만, 갱신이 반복될수록 균형이 서서히 깨져가므로 정기적으로 인덱스 재구성 필요
Index가 특히 유용한 경우
- 실제 SQL 문 실행 시 백그라운드로 임시 영역에 정렬을 수행하는 경우 존재 ➡️ 성능 저하의 원인이 될 수 있음
GROUP BY
, 집약 함수(COUNT
/SUM
/AVG
등), 집합 연산(UNION
/INTERSECT
/EXCEPT
)
- 해당 키 열에 인덱스가 존재하면 옵티마이저가 정렬 과정을 건너뛰도록 효율화 작업 수행
Index 작성이 역효과가 나는 경우
- 과한 인덱스 생성은 성능 개선에 효과가 없을 뿐더러 악영향을 미칠 수 있음
- 갱신이 빈번한 열에 인덱스를 생성하는 경우
- 기존 데이터에 대한 갱신, 제거가 실행되면 자동으로 인덱스도 갱신됨
- 인덱스 갱신으로 인한 오버헤드로 갱신 처리의 성능이 떨어지게 됨
- SELECT 문을 고속화하는 것은 갱신 SQL을 늦추는 Trade off를 가짐
- 의도와 다른 인덱스가 사용되는 경우
- 한 개의 테이블에 복수의 인덱스를 작성한 경우에 발생 가능
- 옵티마이저가 만능은 아니기 때문에 때때로 예측을 빗나가기도 함
- 인덱스를 생성하는 만큼 저장소의 용량을 소비
- 인덱스가 백업 대상에 포함되는 경우 백업 시간이 길어짐
Index 생성 기준
- 크기가 큰 테이블에만 인덱스 생성
- 크기가 작은 테이블은 풀 스캔과 별 차이가 나지 않음
- PK 제약이나 Unique 제약이 있는 열에는 불필요
- PK 제약과 Unique 제약이 있는 열에는 암묵적으로 인덱스가 생성됨
- 값의 중복 체크를 위한 데이터 정렬이 발생하는데, 이를 줄이기 위해 인덱스가 자동 생성됨
- Cardinality가 높은 열에 인덱스 생성
- Cardinality: 값의 분산도. Cardinality가 높을수록 값의 종류가 많음
- Cardinality가 낮은 열에서는 인덱스 트리를 따라가는 조작이 증가할수록 오버헤드 증가
주의 사항
아무리 옵티마이저가 플랜을 잘 세워주더라도 갱신되지 않은 낮은 정밀도의 통계 정보가 사용된다면 최적의 액세스 경로를 선택할 수 없음
- 결과 정보 갱신이 OFF 되어있지는 않은가?
- 통계 정보의 갱신 방법을 OFF로 설정한 경우 테이블의 데이터가 아무리 변경되어도 통계 정보가 갱신되지 않음
- 정기 갱신을 설정한 후로 데이터가 대량으로 변경되었는가?
- 정기 갱신 이후 데이터가 급격하게 변한 경우 다음 갱신 시점까지 이전의 통계 정보가 사용됨
- 이를 위해 통계 정보 자동 수집 설정이 가능하지만 이는 오히려 인덱스 갱신 오버헤드와 유사한 문제를 발생시키기도 함
통계 정보를 올바르게 수집하도록 명심하여야 함
예외적인 경우에는 옵티마이저 힌트, DBMS 파라미터를 이용한 실행 계획 명시적 제한, 통계 정보 처리 일시 보류 등의 대책을 사용하여야 함