[데이터베이스] 데이터베이스 성능 개선
데이터베이스 성능 개선과 관련된 질문을 받았지만 할 수 있었던 대답은 분산 기법(클러스터링, 레플리케이션, 샤딩)밖에 생각나지 않았다.
가장 기본적으로 인덱스나 옵티마이저도 생각했어야했는데 특정 기술에 대한 정의를 알기전에 그 기술에 왜 사용되는지 먼저 알아야하는 자세가 필요하다.
데이터베이스 인덱스(Database Index)
지정한 column을 기준으로 메모리 영역에 일종의 색인을 생성하는 것.
데이터베이스의 레코드가 늘어날수록 table full scan의 속도는 느려진다. 인덱스를 사용하면 빠른 탐색이 가능하다.
하지만 인덱스는 키 값을 기준으로 정렬된 상태를 유지하기 때문에 검색속도는 빨라질 수 있지만, 삽입, 삭제, 갱신을 느려진다.
1) 인덱스 구조
(1) B-Tree 인덱스
- B-Tree 인덱스는 역트리 형태의 자료구조
- Root와 Branch 노드에는 키 값으로 하위노드들의 데이터 값 범위가 저장되며, value에는 하위노드를 찾는데 필요한 주소 정보가 저장된다.
- Leaf노드의 key 값으로는 해당 데이터의 인덱스 키 값이 저장되며, value에는 key 값에 해당하는 테이블 레코드 주소 (ROWID)가 저장된다.
- Leaf노드는 양방향으로 연결되어있기 때문에 수평 스캔 또는 범위 스캔이 가능하다.
(2) 탐색
수직 탐색 - 해당하는 키값을 찾기 위해 Leaf 노드까지 찾아가는 과정이다 (Root -> branch -> leaf)
수평 탐색 - leaf 노드는 양방향으로 연결되어 있기 때문에 일정 범위 또는 전 범위를 leaf 노드 간 이동하면서 탐색하는 과정이다
2) 인덱스 탐색 방식
(1) Index Range Scan
- Index Root 노드에서 Leaf 노드까지 수직 탐색 후, Leaf 노드를 필요한 범위만 스캔
(2) Index Full Scan
- 수직 탐색 없이 Leaf 노드를 처음부터 끝까지 수평탐색
- 첫 번째 Leaf 노드를 찾아가기 위한 수직 탐색 1회 발생
- 최적의 인덱스가 없을 때 차선으로 선택
(3) Index Unique Scan
- 수직 탐색만으로 데이터를 찾는 스캔 방식
- Unique Index를 '=' 조건으로 찾는 경우
(4) Index Skip Scan
- Root 노드 또는 Branch 노드에서 읽은 컬럼 값 정보를 이용해 조건에 부합하는 레코드를 포함할 '가능성 있는' 하위 노드만 골라서 탐색
옵티마이저(Optimizer)
인덱스의 유무, 데이터 분산 또는 편향 정도의 통계 정보를 참조해 여러 실행 계획을 세우고 비용의 연산해 가장 낮은 비용을 가진 실행 계획을 선택하는 DBMS의 핵심 엔진
1) 실행 계획
- SQL에서 요구한 사항을 처리하기 위한 절차와 방법
- 동일한 SQL에 대해 결과를 내는 방법은 여러 가지지만 각 비용은 서로 다름
2) 사용 이유
- DB에서 데이터 조회나 저장에 많은 시간을 소모
- 그러므로 옵티마이저를 통해 쿼리에 소요되는 시간을 줄이고 DB 성능을 향상
3) 한정된 통계 자료로 최적화를 못할 경우?
수동으로 해줘야함
4) 옵티마이저의 목표
a. 전체 처리속도 최적화
b. 최초 응답속도 최적화
5) SQL 최적화 과정
a. 쿼리 수행을 위해 후보군이 될만한 실행 계획 검색
b. 데이터 딕셔너리에 미리 수집한 오브젝트 동계 및 시스템 통계 정보를 이용해 각 실행 계획의 예상 비용 선정
c. 각 실행계획 비교후 최저비용 하나를 선택
6) 서브엔진 별 역할
a. 파서(Parser)
문장 개별 요소 분석하고 파싱해서 파싱 트리 생성
b. 옵티마이저(Optimizer)
- Query Transformer : 파싱된 SQL을 표준적인 형태로 변환
- Estimator : 오브젝트 및 시스템 통계를 기반으로 실행 계획 전체에 대한 총 비용 계산
- Plan Generator : 후보군이 될만한 실행 계획 생성
c. Row-Source Genrator
옵티마이저의 실행 계획을 SQL Engine이 실행 가능한 코드 형태로 변환하는 작업 수행
d. SQL Engine
SQL문 실행
클러스터링(Clustering)
DB 분산 기법 중 하나로 DB 서버를 여러 개 두어 서버 한 대가 죽었을 때 대비할 수 있는 기법
1) Active - Active Clustering
DB 서버를 여러 개 구성하는 데, 각 서버를 Active 상태로 둠
(+) 서버 하나가 죽더라도 다른 서버가 역할을 바로 수행하므로 서비스 중단이 없음
(+) CPU와 메모리 이용률을 올릴 수 있음
(-) 저장소 하나를 공유하게 되면 병목현상이 발생할 수 있음
(-) 서버를 여러대 한꺼번에 운영하므로 비용이 더 발생
2) Active - Standby Clustering
서버를 하나만 운영하고 나머지 서버는 Standby 상태로 둠
운영하고 있는 서버가 다운되었을 시에 Standby 상태의 서버를 Active상태로 전환
(+) Active-Active 클러스터링에 비해 적은 비용
(-) 서버가 다운되었을 때 Standby 상태의 서버를 Active상태로 전환 시 시간이 듬
레플리케이션(Replication)
DB Storage가 손상 또는 손실되었을 때의 대안으로 Storage를 복제하는 기법
1) 단순 백업
- Master DB에 CRUD Operation이 수행될 때마다 Slave DB에 바이너리 로그를 전달해 데이터 동기화 하는 방식
- Master DB가 손실되었을 때, Slave DB를 Master DB로 승격시키므로써 백업 기능을 수행
2) 부하 분산
- 기존 단순 백업 모델에서 Select 작업을 Slave DB에서 수행함으로써 Master DB의 부하를 분산
샤딩(Sharding)
수평 파티셔닝이라고도 함
데이터베이스 테이블에 저장된 레코드의 수가 늘어남에 따라 검색 성능이 떨어지거나 용량에 커짐
샤딩이란 테이블을 row 단위로 나눠서 저장해 DB 용량을 줄이고 검색을 성능을 올리는 기법
즉, 기존 테이블와 똑같은 스키마를 가지는 샤드라는 작은 단위로 나눠 저장하는 방식이다
'샤드키'는 나눠진 샤드 중 어떤 샤드를 선택할 지 결정하는 키로, 샤드키 결정 방식에 따라 샤딩 방법이 나뉘게 된다.
1) 해시 샤딩(Hash Sharding)
해시 함수를 사용해 샤드 키를 나누는 방식
(+) 구현이 간단
(-) 샤드가 늘어나게 되면 해시 함수를 재설정해야하고, 해시 함수를 바꾸게 되면 기존에 저장된 샤드 키도 전부 바꿔야함
(-) 즉, 확장성이 좋지 않음
2) 다이나믹 샤딩(Dynamic Sharding)
Locator Service를 통해 샤드키를 구성하는 방식
(+) 샤드가 더 추가 된다해도 Locator Service 내 샤드키만 추가해 확장 가능
(-) Locator Service에 종속적
3) Entity Group
관계가 되어있는 엔티티를 같은 샤드내에 구성하는 방식
(+) 단일 샤드 내 쿼리가 효율적
(+) 단일 샤드 내 강한 응집도
(-) 다른 shard의 Entity가 연관되어 있는 경우 비효율적
다른 추가적인 방법
(출처 : 코문 선생님 ^^ https://codingmoonkwa.tistory.com/)
1) 옵티마이저 플랜을 보고 힌트를 부여해 인덱스를 강제하거나 조인 순서를 변경해 IO 비용을 줄임
2) 페이징함수(Count over)는 윈도우 함수로써 실행될 때마다 메모리에 적재되므로 출력 직전에 사용이 바람직
3) 패키지는 오라클 cost에 무리가 가능 경우가 있으니 사용하지 않는 것을 권장
4) 큰 데이터 처리나 배치 시스템은 프로시저를 사용해 오라클과 JVM 사이에 데이터 교환을 줄임