SQL 튜닝

박용민·2024년 10월 9일

SQL 튜닝

SQL 최적화

SQL 파싱

  • 파싱 트리 생성 : SQL 문을 이루는 개별 구성 요소를 분석해서 파싱 트리 새성
  • Syntax 체크 : 문법적 오류가 없는지 확인
  • Semantic 체크 : 의미상 오류가 없는지 확인

SQL 최적화

  • 옵티마이저가 역할을 담당
  • 미리 수집한 시스템 및 오브젝트 통계정보를 바탕으로 다양한 실행경로를 생성해서 비교한 후 가장 효율적인 하나를 선택

로우 소스 생성

  • 실제 실행 가능한 코드 또는 프로시저 형태로 포맷팅

SQL 옵티마이저

SQL 옴티마이저는 사용자가 워하는 작업을 가장 효율적으로 수행할 수 잇는 최적의 데이터 경로를 선택
1. 쿼리를 수행하며 후보군이 될만한 실행계획 탐색
2. 미리 수집해둔 통계정보를 이용해 각 실행계획의 예상비용을 산정
3. 최저 비용을 나타내는 실행계획 선택

실행계획 비용

옵티마이저가 생성한 처리절차를 사용자가 확인할 수 있게 트리 구조로 표현한 것

출처 : https://blog.ex-em.com/1544

옵티마이저 힌트

  • 개발자가 직접 더 효율적인 액세스 경로를 찾아낼 수 있다.
  • 주석 기호에 '+'를 추가하면 된다.
SELECT /*+ INDEX(table_name index_name) */ * FROM table_name WHERE column_name = 'value';

SGA(System Global Area)

Oracle 데이터베이스에서 사용하는 공유 메모리 영역으로, 인스턴스의 데이터 및 상태 정보를 저장하는 데 사용됩니다. SGA는 여러 사용자가 동시에 데이터베이스에 액세스할 수 있도록 하며, 데이터베이스 성능에 중요한 역할

구성 요소설명
Database Buffer Cache디스크에서 읽은 데이터 블록을 저장하는 공간으로, 사용자가 데이터를 읽거나 쓸 때 이 버퍼 캐시를 통해 빠르게 액세스할 수 있으며, 자주 사용되는 데이터를 관리 및 디스크 I/O를 줄이고 데이터 접근 속도를 향상
Shared PoolSQL 명령문, PL/SQL 코드, 데이터 딕셔너리 정보를 저장하는 공간으로, 쿼리를 재사용할 수 있게 만듭니다.
Library CacheSQL 및 PL/SQL 명령문을 저장하고 관리합니다.
Data Dictionary Cache테이블, 인덱스 등의 메타데이터 정보를 저장합니다.
Redo Log Buffer데이터 변경 사항을 기록하는 버퍼로, 트랜잭션이 완료되기 전에 변경 사항을 로그로 기록해 장애 복구 시 데이터 복구를 지원하며, 시스템 장애 발생 시 데이터의 일관성을 유지합니다.
Large Pool백업 및 복구 작업, 병렬 쿼리 등에서 사용하는 메모리 영역으로, 메모리의 부담을 줄입니다.
Java PoolJava 애플리케이션 및 스토어드 프로시저를 위한 메모리 공간입니다.
Streams PoolOracle Streams 기능을 위한 메모리 영역으로, 데이터 복제 및 흐름 관리를 담당합니다.
In-Memory Area데이터를 메모리 내에서 열 형식으로 저장하여 OLAP와 같은 데이터 분석 작업에 최적화됩니다.

출처 : https://docs.oracle.com/en/database/oracle/oracle-database/23/dbiad/db_sga.html

소프트 파싱, 하드 파싱

  • SQL 파싱, 최적화, 로우 소스 생성 과정을 거쳐 생성한 내부 프로시저를 반복 재사용할 수 있도록 캐싱해 두는 메모리 공간을 '라이브러리 캐시'
  • 캐시에서 찾으면 곧바로 실행 단계로 넘거가고 찾지 못하면 최적화 단계를 거친다.
  • 캐시에서 찾아 곧바로 실행단계로 넘어가는 것을 '소프트 파싱', 찾는 데 실패해 최적화 및 로우 소수 생성 단계까지 모두 거치는 것을 '하드 파싱'

데이터베이스 저장 구조

  • 테이블스페이스 : 세그먼트를 담는 콘테이너로, 여러 개의 데이터파일로 구성
  • 세그먼트 : 테이블, 인덱스처럼 데이터 저장공간이 필요한 오브젝트
  • LOB : 그 자체 하나의 세그먼트를 구성, 별도 공간에 값을 저장
  • 한 블록은 하나의 테이블이 독점하며 저장된 레코드는 모두 같은 테이블 레코드이다.
  • 세그먼트 공간이 부족해지면 테이블스페이스로부터 익스텐트를 추가로 할당


출처 : https://docs.oracle.com/cd/B10500_01/server.920/a96524/c04space.htm

시퀀셜 액세스

  • 시퀀셜 액세스 : 논리적 또는 물리적으로 연결된 순서에 따라 차례대로 블록을 읽는 방식
  • 인덱스 리프 블록은 아뒤를 가리키는 주소값을 통해 논리적으로 서로 연결
  • 각 인스텐트의 첫 번째 블록 뒤에 연속해서 저장된 블록을 순서대로 읽으면, Full Table Scan

랜덤 액세스

  • 레코드 하나를 읽기 위해 한 블록씩 접근하는 방식

논리적, 물리적 I/O

DB 버퍼 캐시

  • '데이터 캐시'로 디스크에서 읽은 데이터 블록을 캐싱하여 반복적인 I/O Call을 줄이는데 목적
  • 서버 프로세스와 데이터파일 사이에 버퍼캐시가 있으며 데이터 블록을 읽을 땐 항상 버퍼캐시부터 탐색
  • 버퍼캐시는 공유메모리 영역이므로 같은 블록을 읽는 다른 프로세스도 이득

논리적 I/O

  • SQL을 처리하는 과정에서 발생하는 총 블록 I/O
  • 메모리상의 버퍼 캐시를 경유하므로 메모리 I/O가 곧 논리적 I/O

물리적 I/O

  • SQL 처리 도중 읽어야 할 블록을 버퍼캐시에서 찾지 못할 때만 디스크를 엑세스 하므로 논리적 블록 I/O 중 일부를 물리적 I/O라고 함
  • 메모리 I/O에 비해 상당히 느림

Direct Path Read 방식으로 읽는 경우를 제외하면 모든 블록은 DB 버퍼캐시를 경유해서 읽는다.

Single Block I/O

Single Block I/O : 한 번에 한 블록씩 요청해서 메모리에 적재하는 방식, 인덱스를 이용할 때 기본적으로 사용

Multiblock I/O

Multiblock I/O : 한 번에 여러 블록씩 요청해서 메모리에 적재하는 방식, 테이블 전체를 스캔할 때 사용

캐시 탐색 매커니즘

Direct Path I/O를 제외한 모든 블록 I/O는 메모리 버퍼캐시를 경유

Latches : 멀티스레드 환경에서 데이터의 일관성을 유지하고, 동시성 문제를 방지하기 위해 사용되는 경량 잠금 메커니즘
Hash buckets : 해시 테이블의 구성 요소로, 특정 키 값을 가진 데이터를 저장하는 공간
Buffer headers : 데이터베이스 버퍼 캐시에서 각 데이터 블록에 대한 메타데이터를 저장하는 구조, 블록의 상태 및 위치 정보를 포함

인덱스

LMC(Leftmost Child)

자식 노드중 가장 왼쪽 끝에 위치한 블록 리프 블록에 저장된 각 레코드는 키값 순으로 정렬 테이블 레코드를 가르키는 주소값, RoWID를 갖으며 인덱스 키값이 같으면 ROWID 순으로 정렬

  • ROWID = 데이터 블록 주소 + 로우 번호
  • 데이터 블록 주소 = 데이터 파일 번호 + 블록 번호
  • 블록 번호 : 데이터파일 내에서 부여한 상대적 순번
  • 로우 번호 : 블록 내 순번

수직적 탐색

이동한 브랜치 블록에는 찾고자 하는 값과 정확히 일치하는 레코드가 있으면 하위 블록으로 이동하면 안되고 바로 직전 레코드를 가르키는 하위 블록으로 이동

수평적 탐색

수직적 탐색을 통해 스캔 시작점을 찾았으면 수평적으로 리프 블록을 스캔한다. 인덱스를 정상적으로 사용한다는것은 리프 블록에서 스캔 시작점을 찾아 거기서부터 스캔하다가 중간에 멈추는 것 인덱스 선두 컬럼이 가공되지 않은 상태로 조건절에 있으면 인덱스 Range Scan이 가능

Table Full Scan

  • 시퀀셜 액세스 및 Multiblock I/O로 디스크 블록을 읽음
  • 한 블록에 속한 모든 레코드를 한 번에 읽고 캐시 미스시 I/O Call를 통해 블록을 한번에 I/O
  • 큰 테이블에서 소량 데이터를 검색할때 반드시 인덱스를 사용


출처 : https://docs.oracle.com/en/database/oracle/oracle-database/23/tgsql/optimizer-access-paths.html#GUID-3D9D3DBD-CF89-4320-8C5E-6BE6A127AC39

Index Range Scan

  • 랜덤 엑세스와 Single Single Block I/O 방식으로 디스크 블록을 읽음
  • 캐스 미스시 레코드 하나를 읽기 위해 매번 Block
  • 각 블록을 단 한 번 읽는 Table Full Scan보다 훨씬 불리


출처 : https://docs.oracle.com/en/database/oracle/oracle-database/23/tgsql/optimizer-access-paths.html#GUID-3D9D3DBD-CF89-4320-8C5E-6BE6A127AC39

Unique Index Scan

  • 인덱스의 특정 값에 직접 접근하므로, 전체 테이블을 스캔하는 것보다 훨씬 빠르게 해당하는 조건을 만족하는 정확히 하나의 행만 반환
  • 필요하지 않은 데이터 블록에 접근하지 않으므로, I/O 작업이 최소화


출처 : https://docs.oracle.com/en/database/oracle/oracle-database/23/tgsql/optimizer-access-paths.html#GUID-3D9D3DBD-CF89-4320-8C5E-6BE6A127AC39

인덱스 튜닝

인덱스를 스캔하는 이유는 검색 조건을 만족하는 소량의 데이터를 인덱스에서 빨리 찾고 테이블 레코드를 찾아가기 위한 주소값 즉 ROWID를 얻기 위함(논리적 주소)

I/O 메커니즘

I/O 성능을 높이려면 버퍼캐시를 활용, 디스크로 가기 전에 버퍼캐시부터 검색, 못 찾을 떄만 디스크에서 블록을 검색

인덱스 클러스터링 팩터

  • 특정 컬럼을 기준으로 같은 값을 갖는 데이터가 서로 모여있는 정도
  • 좋은 컬럼에 생성한 인덱스는 검색 효율이 매우 좋음
  • 오라클은 래치 획득과 해쉬 체인 스캔 과정을 거쳐 어렵게 찾아간 테이블 블록에 대한 포인터를 바로 해제하지 않고 유지하는데 이를 버퍼 Pinning이라고 하며 만약 직전과 같은 테이블 블록을 가르키면 래치 획득과 해시 체인 스캔 과정을 생략하고 바로 테이블 블록을 읽음

인덱스 탐색

LMC

루트 블록에는 키 값을 갖지 않는 특별한 레코드를 의미하며 가장 왼쪽에 있다고 해서 키 값을 가진 첫 번째 레코드

엑세스 조건, 필터조건

  • 인덱스 엑세스 조건은 인덱스 스캔 범위를 결정하는 조건, 수직적 탐색을 통해 스캔 시작점을 결정하고 리프 블록을 스캔하다 어디서 멈출지 결정
  • 인덱스 필터 조건은 테이블로 엑세스할지를 결정
  • 테이블 필터 조건은 쿼리 수행 다음 단계로 전달하거나 최종 결과집합에 포함할지를 결정

군집성

테이블과 달리 인덱스에는 같은 값을 갖는 레코드들이 서로 군집

  • 첫 번쨰 나타나는 범위검색 조건까지만 만족하는 인덱스 레코드는 모두 연속해서 모여 있으나 그 이하 조건까지 만족하는 레코드는 비교 연산자 종류에 상관없이 흩어짐
  • 가장 선두 컬럼이 범위검색 조건이면, 그 조건이 스캔 범위를 결정하며 이들을 인덱스 엑세스 조건이고 나머지를 인덱스 필터 조건이다.


출처 : http://www.gurubee.net/wiki/pages/31752196

엑세스 불충분한 조건

인덱스 스캔 효율서은 인덱스 컬럼을 조건절에 모두 등치(=) 조건으로 사용할 떄 가장 좋다.

  • 좌변 컬럼을 가공한 조건
  • % like %
  • 같은 컬럼에 대한 조건절이 두 개 이상일 때, 선택되지 못한 조건절
  • OR Expansion 또는 INLIST ITERATOR로 선택되지 못한 OR 또는 IN 조건

BETWEEN 조건을 IN-List로 전환시 주의 사항

  • IN-List 개수가 많으면 수직적 탐색이 많이 발생
  • BETWEEN 조건 떄문에 리프 블록을 많이 스캔 < IN-List 개수만큼 브랜치 블록을 반복 탐색
  • 인덱스 스캔 과정에 선택되는 레코드들이 서로 멀리 떨어져 있을 때만 유용

IN 조건은 '='이 아니다

  • IN 조건이 '='이 되려면 IN-List Iterator 방식으로 풀려야하며 그렇지 않으면 필터 조건으로 수행
  • NUM_INDEX_KEYS 힌트 활용으로 엑세스 조건 또는 필터 조건으로 유도

BETWEEN, LIKE 스캔 범위

  • LIKE보다 BETWEEN을 사용하는것이 좋음
where 판매월 between '201901' and '201912'
	and 판매구분 = 'B'

where 판매월 like '2019%'
	and 판매구분 = 'B'

where 판매월 between '201901' and '201912'
	and 판매구분 = 'A'

where 판매월 like '2019%'
	and 판매구분 = 'A'

옵션 조건 처리 방식

OR 조건

# table full scan
select * from 거래
where(:cust_id is null or 고객ID = :cust_id)
and 거래일자 between :dt1 and :dt2
  • 옵티마이저에 의한 OR Expansion 쿼리 변환이 기본적으로 작동하지 않으므로 선두에 OR 조건을 사용 x
  • 인덱스 스캔 단계에서 필터링해도 테이블 엑세스 단계에서 필터랑 하여 비효율
  • 필수 조건이 '='이면 옵션이면 인덱스 엑세스 조건이므로 최적의 성능 구현
--- 인덱스 : 상품명 + 상품분류코드
select * from 상품
where 상품명 = :prd_nm
and 상품분류코드 like :prd_cls_cl || '%'

LIKE/BETWEEN 패턴

  • 인덱스 선두 컬럼에 대한 옵션 조건을 LIKE/BETWEEN 연산자로 처리 x
  • NULL 허용 컬럼에 대한 옵션 조건을 LIKE/BETWEEN 연산자로 처리 x
  • 숫자형이면서 인덱스 액세스 조건으로도 사용 가능한 컬럼에 대한 옵션 조건 처리 LIKE 방식 x
  • LIKE를 옵션 조건에 사용시 컬럼 값 길이가 고정적

인덱스 설계시 가장 중요한 2가지

  • 조건절에 항상 사용하거나, 자주 사용하는 컬럼을 선정
  • '=' 조건으로 자주 조회하는 컬럼을 앞쪽에 배치

조인 튜닝

NL 조인

Nested Loop Join으로 한 테이블에서 다른 테이블의 모든 행을 탐색
테이블에서 하나의 행을 선택하는 Outer Loop, 조인 조건에 맞는 모든 행을 순회하는 Inner Loop

Inner 쪽 테이블은 인덱스를 사용해야 한다. Inner 루프에서 인덱스를 사용하지 않으면 Table Full Scan을 반복한다.


출처 : https://www.interdb.jp/pg/pgsql03/05/01.html

  • 랜덤 액세스 위주의 조인 방식
  • 조인을 한 레코드씩 순차적으로 진행
  • NL 조인은 소량 데이터를 주로 처리하거나 부분범위 처리가 가능한 온라인 트랜잭션 처리 시스템에 적합
  • 테이블 Prefetch를 이용해 테이블을 엑세스 하닥 디스크 I/O가 필요해지면 미리 읽어서 버퍼캐시에 적재하는 기능

소트 머지 조인

SGA,PGA

  • SGA에 공유된 데이터를 읽고 쓰면서, 도잇에 자신만의 고유 메모리 영역을 보유
  • 오라클 서버 프로세스에 할당된 메모리 영역을 PGA(Process/Program/Private Global Area)라고 부르며, 프로세스에 종속적인 고유 데이터를 저장하는 용도
  • 할당 받은 PGA 공간이 작아 데이터를 모두 저장할 수 없을 때 Temp 테이블스페이스를 이용

PGA는 다른 프로세스와 공유하지 않는 독립적인 메모리 공간이므로 래치 메커니즘이 불필요 따라서 같은 양의 데이터를 읽어도 SGA 버퍼캐시에서 읽을 때 보다 훨씬 빠름

구분SGA (System Global Area)PGA (Program Global Area)
역할인스턴스 전체에서 공유되는 메모리 영역개별 사용자 세션에서 사용되는 비공유 메모리 영역
구성 요소데이터 버퍼 캐시, 공유 풀, 대기열, 로그 버퍼 등정렬 영역, 해시 영역 등
관리 주체데이터베이스 인스턴스서버 프로세스 또는 백그라운드 프로세스
공유 여부여러 사용자 세션 간 공유각 세션 별로 독립적
주요 기능데이터 및 SQL 문 캐싱, 데이터베이스 동시성 제어 지원세션별 SQL 작업 처리, 정렬 작업, 해시 조인 처리
  1. 소트 단계 : 양쪽 집합을 조인 컬럼 기준으로 정렬하고 Sort Area에 저장
  2. 머지 단계 : 정렬한 양쪽 집합을 서로 Merge 수행
  • 조인 과정에서 액세스하는 모든 블록을 랜덤 액세스 방식으로 가끔 DB 버퍼캐시를 경유해서 읽음


출처 : https://logicalread.com/oracle-11g-sort-merge-joins-mc02/

해시 조인

  1. Build 단계 : 작은 쪽 테이블을 읽어 해시 테이블 생성
  2. Probe 단계 : 큰 쪽 테이블을 읽어 해시 테이블을 탐색하면서 조인
  • 해시 테이블을 PGA 영역에 할당하여 래치 획득 과정 없이 PGA에서 빠르게 데이터를 탐색하고 조인
  • PGA는 큰 메모리 공간이 아니므로 두 집합 중 어느 하나가 중대형 이상이면 디스크에 쓰는 작업을 반드시 수반
  • 해시 테이블은 단 하나의 쿼리를 위해 생성하고 조인이 끝나면 소멸하는 자료구조 이기에 수행시간이 짧으면서 수행빈도가 높은 쿼리에는 적합하지 않음


출처 : https://www.alibabacloud.com/help/en/polardb/polardb-for-mysql/user-guide/hash-joins-in-parallel-queries

서브쿼리 조인

쿼리 변환은 옵티마이저가 SQL을 분석해 의미적으로 동일하면서도 더 나은 성능이 기대되는 형태로 작성하는 것

  • 인라인 뷰(Inline View) : From 절에 사용한 서브쿼리
SELECT department_id, employee_id, name, salary
FROM (SELECT department_id, employee_id, name, salary,
             ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rank
      FROM employees) AS ranked_employees
WHERE rank = 1;
  • 중첩 서브쿼리(Nested Subquery) : 결과집합을 한정하기 위해 WHERE 절에 사용한 서브쿼리
SELECT employee_id, name, salary
FROM employees
WHERE salary > (SELECT AVG(dept_avg_salary)
                FROM (SELECT department_id, AVG(salary) AS dept_avg_salary
                      FROM employees
                      GROUP BY department_id) AS department_averages);
  • 스칼라 서브쿼리(Scalar Subquery) : 한 레코드당 정확히 하나의 값을 반환하는 서브 쿼리
SELECT employee_id, name, salary,
       salary - (SELECT AVG(salary) FROM employees) AS salary_difference
FROM employees;

소트 연산

메모리 공간인 Sort Area가 다 차면, 디스크 Temp 테이블스페이스를 활용

  • 메모리 소트 : 전체 데이터의 정렬 작업을 메모리 내에서 완료 Internal Sort
  • 다스크 소트 : 할당받은 Sort Area 내에서 정렬을 완료하지 못해 디스크 공간까지 사용하는 겨우

부분범위 처리

  • 쿼리 수행 결과 중 앞쪽 일부를 우선 전송하고 멈추었다 클라이언트가 요청을 요청할 때마다 남은 데이터를 조금씩 나눠 전송하는 방식
  • 클라이언트가 특정 DB 커넥션을 독점할 수 없다. 따라서 단위 작업을 마치면 DB 커넥션을 바로 커넥션 풀에 반환해야 하므로 그 전에 쿼리 조회 결과를 클라이언트에게 '모두' 전송하고 커서(Cursor)를 닫아야함

DML 튜닝

Redo 로깅

데이터파일과 컨트롤 파일에 가해지는 모든 변경사항을 Redo 로그에 기록하는데 트랜잭션 데이터가 어떤 이유에서건 유실됐을 때, 트랜잭션을 재현함으로써 유실 이전 상태로 복구하는 데 사용
DML을 수행할 때마다 Redo 로그를 생성해야 하므로 Redo 로깅은 DML 성능에 영향을 미치기에 INSERT 작업에 대해 Redo 로깅 생략 기능을 제공하는 이유

  • 물리적으로 디스크가 깨지는 등의 Media Fail 발생 시 DB를 복구하기 위해 사용
  • DBMS가 버퍼캐시를 도입하는 이유는 I/O 성능을 높이기 위해, 버퍼캐시는 휘발성 인스턴스가 비정상적으로 종료되면, 그때까지의 작업내용을 모두 읽게 된다. 트랜잭션 데이터 유실에 대비하기 위해 Redo 로그를 남김
  • 디스크 상의 데이터 블록에 반영하는 작업은 랜덤 액세스 방식으로 이루어지므로 매우 느리기에 트랜잭션에 의한 변경사항을 우선 Append 방식으로 빠르게 로그 파일에 기록

Undo 로깅

과거에는 Rollback이라는 용어를 주로 사용했지만, 오라클은 Undo라는 용어를 사용
Undo는 트랜잭션을 롤백함으로써 현재를 과거 상태로 되돌리는데 사용, 변경된 블록을 이전 상태로 되돌리는 데 필요한 정보를 로깅

  • 트랜잭션에 의한 변경사항을 최종 커밋하지 않고 롤백하고자 할 때 Undo 데이터를 이용, 시스템이 셧다운된 시점에 아직 커밋되지 않았던 트랜잭션들을 모두 롤백해야하는 상황에 일기 일관성을 위해 사용
  • 마지막 커밋이 발생한 시점 정보를 SCN이라는 Global 변수값으로 관리하는데 각 블록이 마지막으로 변경된 시점을 관리하기 위해 모든 블록 헤더에 SCN을 기록

트랜잭션 데이터 저장 과정

  1. DML 문을 실행하면 Redo 로그버퍼에 변경사항을 기록
  2. 버퍼블록에서 데이터를 변경, 버퍼캐시에서 블록을 찾지 못하면, 데이터파일에서 읽는 작업부터 수행
  3. commit
  4. LGWR(Log Writer) 프로세스가 Redo 로그버퍼 내용을 로그파일에 일괄 저장
  5. DBWR(Database Writer) 프로레스가 변경된 버퍼블록들을 데이터파일에 일괄 저장


출처 : https://databaseinternalmechanism.com/oracle-database-internals/oracle-process-architecture/

DB Call

  • Parse Call : SQL 파싱과 최적화를 수행, SQL과 실행계획을 라이브러리 캐시에서 찾으면, 최적화 단계는 생략
  • Execute Call : DML은 이 단계에서 모든 과정이 끝나지만, SELECT 문은 Fetch 단계를 거친다.
  • Fetch Call : 데이터를 읽어서 사용자에게 결과집합을 전송하는 과정

Direct Path I/O

일반적인 블록 I/O는 DB 버퍼캐시를 경유하는데 읽고자 하는 블록을 먼저 버퍼캐시에서 찾아보고, 찾지 못할 때만 디스크에서 읽고 데이터를 변경할 때도 먼저 블록을 버퍼캐시에서 찾고 찾은 버퍼블록에 변경을 가하고 나면, DBWR 프로세스가 변경된 블록(Dirty 블록)들을 주기적으로 찾아 데이터파일에 반영
블록을 재사용할 가능성이 있느냐도 중요한데, Full Scan 위주로 가끔 수행되는 대용량 처리 프록램이 읽어 들인 데이터는 대개 재사용이 낮음

Lock, 동시성 제어

Insert에 대한 로우 Lock 경합은 Unique 인덱스가 있을 떄만 발생
MVCC 모델을 사용하지 않는 DBMS는 SELECT 문에 공유 Lock를 사용 두 트랜잭션이 같이 Lock를 설정할 수 있는 의미 이며 공유 Lock, 배타 Lock은 호환되지 않기에 DML과 SELECT가 서로 진행을 방해할 수 있음

테이블 Lock라고 하면, 테이블 전체를 Lock으로 생각하지만 자신이 해당 테이블에서 현재 어떤 작업을 수행 중인지를 알리는 일종의 Flag
어떤 모드를 사용했는지에 따라 후행 트랜잭션이 수행할 수 있는 작업의 범위가 결정

비관적 Lock

사용자들이 같은 데이터를 동시에 수정할 것으로 가정하며 한 사용자가 데이터를 읽는 시점에 Lock을 걸고 조회 또는 갱신 처리가 완료될 대까지 유지

낙관적 lcok

사용자들이 같은 데이터를 동시에 수정하지 않을 것으로 가정 데이터를 읽을 때 Lock을 설정하지 않음

채번 테이블

각 테이블의 식별자의 단일컬럼 일련번호 또는 구분 속성별 순번을 채번하기 위해 별도 테이블을 관리하는 방식
직렬화가 이루어지므로 두 트랜잭션이 중복 값을 채번할 가능성을 원천적으로 방지

시퀀스 오브젝트

INSERT 과정에 중복 레코드 발생에 대비한 예외처리에 크게 신경쓰지 않아도 되며 성능이 빠름

SQL 옵티마이저

  • 옵티마이저 모드를 포함해 각종 파라미터를 적절한 값으로 설정하고, 통계정보를 잘 수집해 주는 것이 중요
  • 전략적인 인덱스 구성이 필수적
  • DBMS가 제공하는 기능을 적극적으로 활용해 옵티마이저가 최적의 선택을 할 수 있도록 다양한 수단을 제공

참고자료 : 친절한 SQL 튜닝

🎉Done

0개의 댓글