[SQLD] 2과목 3장 SQL최적화 기본원리

10150 단어 SQLDSQLD

1. 3장 1절 옵티마이저와 실행계획

1) 옵티마이저

👉 사용자가 질의한 SQL문에 대해 최적의 실행방법을 결정하는 역할을 수행한다.
👉 옵티마이저가 선택한 실행방법의 적절성 여부는 질의의 수행속도에 가장 큰 영향을 미치게 된다
👉 실제로 SQL문을 처리해보지 않은 상태에서 최적의 실행방법을 결정해야하는 어려움이 있다.

👉 현재 대부분의 관계형 데이터베이스는 비용기반 옵티마이저만을 제공한다. 다만 하위버전 호환성을 위해서만 규칙기반 옵티마이저 기능이 남아 있을 뿐이다.

2) 옵티마이저 종류

① 규칙기반 옵티마이저(RBO)

👉 우선순위를 가지고 실행계획을 생성한다. 우선 순위가 높은 규칙이 적은 일량으로 해당 작업을 수행한다고 판단한다.
👉 보편 타당성에 근거한 것들이다. 이러한 규칙을 알고 있으면 옵티마이저의 최적화 작업을 이해하는데 도움이 된다.

  • RBO 우선순위
    낮을수록 높은 우선 순위이다. 순위 2~14위까지는 인덱스를 활용하는 방법이다.


1. Single row by rowid
: rowid를 통해서 테이블에서 하나의 행을 액세스하는 방식이다. 하나의 행을 액세스하는 가장 뻐른 방법이다.
4. Single row by unique or primary key
: 유일 인덱스를 통해서 하나의 행을 액세스하는 방식이다. 인덱스를 먼저 액세스하고 인덱스에 존재하는 rowid를 추출하여 테이블의 행을 액세스한다.
8. Composite index
: 복합 인덱스에 동등('=' 연산자) 조건으로 검색하는 경우이다.
9. Single column index
: 단일 칼럼 인덱스에 '='조건으로 검색하는 경우이다.
10. Bounded range search on indexed columns
: 인덱스가 생성되어 있는 칼럼에 양쪽 범위를 한정하는 형태로 검색하는 방식이다. (BETWEEN, LIKE 연산자)
11. Unbounded range search on indexed columns
: 인덱스가 생성되어 있는 칼럼에 한쪽 범위만 한정하는 형태로 검색하는 방식이다. (>, >=, <, <= 연산자)
15. Full table scan
: 전체 테이블을 액세스하면서 조건절에 주어진 조건을 만족하는 행만을 결과로 추출한다. 일반적으로 속도가 느리지만, 병렬처리가 가능하다.

② 비용기반 옵티마이저(CBO)

👉 현재 대부분의 DB에서 사용한다.
👉 SQL문을 처리하는데 필요한 비용이 가장 적은 실행계획을 선택하는 방식이다.
👉 테이블, 인덱스, 칼럼 등의 다양한 객체 통계정보와 시스템 통계 정보 등을 이용한다.
👉 통계 정보가 없는 경우 비용기반 옵티마이저는 정확한 비용 예측이 불가능해져서 비효율적인 실행계획을 생성할 수 있다. 따라서 정확한 통계정보를 유지하는 것은 비용기반 최적화에서 중요한 요소이다.
👉 통계 정보, DBMS 버전, DBMS 설정 정보 등의 차이로 인해 동일 SQL문도 서로 다른 실행계획이 생성될 수 있다.
👉 다양한 한계들로 인해 실행계획의 예측 및 제어가 어렵다는 단점이 있다.

👉 비용 : SQL문을 처리하기 위해 예상되는 소요시간 또는 자원사용량

  • 구성요소 : 질의 변환기, 대안 계획 생성기, 비용 예측기 등의 모듈

3) 실행계획

👉 SQL에서 요구한 사항을 처리하기 위한 절차와 방법을 의미한다.
👉 실행계획을 보고 SQL이 어떻게 실행되는지 정확히 이해할 수 있다면 보다 향상된 SQL 이해 및 활용을 할 수 있다.

  • 구성요소

  1. 조인순서
    : 논리적으로 가능한 조인순서는 n!만큼 존재 (n은 from절에 존재하는 테이블 수)
    현실적으로 옵티마이저가 적용가능한 조인 순서는 n! 보다 작다.

  2. 조인기법
    : 두 개의 테이블을 조인할 때 사용할 수 있는 방법
    NL join, Hash join, Sort Merge join

  3. 액세스기법
    : 하나의 테이블을 액세스할 때 사용할 수 있는 방법
    ✔ 인덱스 스캔 : 인덱스를 이용하여 테이블 액세스
    ✔ 전체 테이블 스캔 : 테이블 전체를 모두 읽으면서 조건을 만족하는 행 찾음

  4. 최적화 정보
    : 실제로 SQL을 실행하고 얻은 결과가 아니라 통계정보를 바탕으로 옵티마이저가 계산한 예상치
    ✔ Cost : 상대적인 비용 정보
    ✔ Card : Cardinality 약자, 주어진 조건을 만족한 결과 집합 혹은 조인 조건을 만족한 결과 집합의 건수
    ✔ Bytes : 결과 집합이 차지하는 메모리 양을 바이트로 표시한 것

  5. 연산
    : 여러가지 조작을 통해서 원하는 결과를 얻어내는 일련의 작업
    조인기법, 액세스기법, 필터, 정렬, 집계, 뷰 등 다양하게 존재

4) SQL 처리 흐름도

👉 SQL 내부적인 처리 절차를 시각적으로 표현한 도표이다.
👉 실행계획을 시각화한 것이다.
👉 SQL문 처리를 위한 조인순서, 테이블 액세스 기법, 조인 기법 등을 표현할 수 있다.

📌 예제)

select ...
from tab1 a, tab2 b
where a.key = b.key
and a.col1 = :조건1
and b.col2 = :조건2

  1. 조인순서는 tab1 -> tab2이다.
  2. 테이블 액세스 방법은 tab1은 전체 스캔을 의미하고, tab2는 I01_tab2이라는 인덱스를 통한 인덱스 스캔을 했음을 표시하였다.
  3. tab1에 대한 액세스는 스캔방식이고 조인시도 및 I01_tab2 인덱스를 통한 tab2 액세스는 랜덤방식이다.
  4. 성능적인 관점을 살펴보기 위해서 처리흐름도에 일량을 함께 표시할 수 있다.
    건수(액세스 건수, 조인시도 건수, 테이블 액세스 건수, 성공 건수)라고 표시된 곳에 SQL 처리를 위해 작업한 건수 또는 처리 결과 건수 등의 일량을 함께 표시할 수 있다. 이것을 통해 비효율이 발생하고 있는 지에 대한 힌트를 얻을 수 있다.

2. 3장 2절 인덱스 기본

1) 인덱스 특징

👉 원하는 데이터를 쉽게 찾을 수 있도록 돕는 책의 찾아보기와 유사한 개념이다.
👉 인덱스는 테이블을 기반으로 선택적으로 생성할 수 있는 구조이다.
👉 검색 조건을 만족하는 데이터를 인덱스를 통해 효과적으로 찾을 수 있도록 돕는다.
👉 DML(insert, update, delete) 작업은 테이블과 인덱스를 함께 변경해야 하기 때문에 오히려 느려질 수 있다는 단점이 존재한다.

2) B트리 기반 인덱스

👉 인덱스에서 원하는 값을 찾는 과정이다.

  1. 브랜치 블록의 가장 왼쪽 값이 찾고자 하는 값보다 작거나 같으면 왼쪽 포인터로 이동 (B-)
  2. 찾고자 하는 값이 브랜치 블록의 값 사이에 존재하면 가운데 포인터로 이동 (B*)
  3. 오른쪽에 있는 값보다 크면 오른쪽 포인터로 이동 (B+)

📌 예제)

  1. 37을 찾고자 한다면 루트 블록에서 50보다 작으므로 왼쪽 포인터로 이동한다. 37은 왼쪽 브랜치 블록의 11과 40 사이의 값이므로 가운데 포인터로 이동한다. 이동한 결과 해당 블록이 리프 블록이므로 37이 블록 내에 존재하는 지 검색한다.

  2. 37과 50 사이의 모든 값을 찾고자 한다면 (BETWEEN 37 AND 50) 위와 동일한 방법으로 리프 블록에서 37을찾고 50보다 큰 값을 만날때 까지 오른쪽으로 이동하면서 인덱스를 읽느느다. 이것은 인덱스가 정렬죄어있고 리프 블록이 양방향 링크로 연결되어 있기 때문에 가능하다.

3) 전체 테이블 스캔

👉 테이블에 존재하는 모든 데이터를 읽어가면서 조건에 맞으면 결과로서 추출하고 조건에 맞지 않으면 버리는 방식으로 검색한다.
👉 oracle의 경우, 검색 조건이 맞는 데이터를 찾기 위해서 테이블의 고수위마크(HWM, High Water Mark) 아래의 모든 블록을 읽는다.
👉 전체 테이블 스캔 방식으로 데이터를 검색할 때 고수위 마크까지 블록 내 모든 데이터를 읽어야 하기 때문에 모든 결과를 찾을 때까지 시간이 오래 걸릴 수 있다.

👉 고수위 마크 : 테이블에 쓰여졌던 블록 상의 최상위 위치 (현재는 지워져서 데이터가 존재하디 않을 수도 있음)

  • 전체 테이블 스캔을 하는 경우
  1. SQL문에 조건이 존재하지 않는 경우
    : SQL문에 조건이 존재하지 않는다는 것은 테이블에 존재하는 모든 데이터가 답이 된다는 것이다.

  2. SQL문의 주어진 조건에 사용 가능한 인덱스가 존재하지 않는 경우
    : 사용가능한 인덱스가 존재하지 않는 다면 데이터를 액세스할 수 있는 방법은 테이블의 모든 데이터를 읽으면서 주어진 조건을 만족하는지 검사하는 방법뿐이다.
    또한 주어진 조건에 사용가능한 인덱스는 존재하나 함수를 이용하여 인덱스 칼럼을 변현한 경우에도 인덱스를 사용할 수 없다.

  3. 옵티마이저의 취사 선택
    : 조건을 만족하는 데이터가 많은 경우, 결과를 추출하기 위해서 테이블 대부분의 블록을 액세스해야 한다고 옵티마이저가 판단하면 조건에 사용가능한 인덱스가 존재해도 전체 테이블 스캔 방식으로 읽을 수 있다.

  4. 그 밖의 경우
    : 병렬처리 방식으로 처리하는 경우 또는 전체 테이블 스캔 방식의 힌트를 사용한 경우에 전체 테이블 스캔 방식으로 데이터를 읽을 수 있다.

4) 인덱스 스캔

👉 인덱스를 구성하는 칼럼의 값을 기반으로 데이터를 추출하는 액세스 기법이다.

① 인덱스 유일 스캔

👉 유일 인덱스를 사용하여 단 하나의 데이터를 추출하는 방식이다.
👉 중복을 허락하지 않고 구성 칼럼에 대해 모두 '='로 값이 주어진 경우에만 가능하다.

② 인덱스 범위 스캔

👉 인덱스를 이용하여 한 건 이상의 데이터를 추출하는 방식이다.
👉 유일 인덱스의 구성 칼럼 모두에 대해 '='로 값이 주어지지 않은 경우와 비유일 인덱스를 이용하는 모든 액세스 방식은 인덱스 범위 스캔으로 데이터를 액세스하게 된다.

③ 인덱스 역순 범위 스캔

: 인덱스의 리프 블록의 양방향 링크를 이용하여 내림차순으로 데이터를 읽는다.

5) 전체 테이블 스캔과 인덱스 스캔 방식의 비교

👉 데이터를 액세스하는 방법은 크게 인덱스를 경유해서 읽는 인덱스 스캔 방식과 테이블의 전체 데이터를 모두 읽으면서 데이터를 추출하는 전체 테이블 스캔 방식으로 나눠 볼 수 있다.
👉 인덱스 스캔 방식은 사용가능한 적절한 인덱스가 존재할 때만 이용할 수 있는 스캔 방식이다.
👉 전체 테이븡 스캔 방식은 인덱스의 존재 유무와 상관없이 항상 이용 하능한 스캔 방식이다.
👉 옵티마이저는 인덱스가 존재하더라고 전체 테이블 스캔 방식을 취사 선택할 수 있다.

3. 3장 3절 조인 수행 원리

1) 조인

👉 두 개이상의 테이블을 하나의 집합으로 만드는 연산이다.
👉 SQL문에서 from절에 두 개 이상의 테이블이 나열된 경우 조인이 수행된다.
👉 조인 연산은 두 테이블(집합) 사이에서 수행된다.
👉 테이블 또는 조인 결과를 이용하여 조인을 수행할 때 조인 단계별로 다른 조인 기법을 사용할 수 있다.

2) NL(Nested Loop) 조인

👉 프로그래밍에서 사용하는 중접된 반복문과 유사한 방식으로 조인을 수행한다.
👉 선행 테입르의 조건을 만족하는 행을 추출하여 후행 테이블을 읽으면서 조인을 수행한다. 이 작업은 선행 테이블의 조건을 만족하는 모든 행의 수 만큼 반복 수행한다.
👉 랜덤 액세스 방식으로 데이터를 읽기 때문에 처리 범위가 좁은 것이 유리하다.

👉 선행 테이블, 외부 테이블 (Outer table) : 반복문의 외부에 있는 테이블
👉 후행 테이블, 내부 테이블 (Inner table) : 반복문의 내부에 있는 테이블

  • 조인 작업 방법
  1. 선행 테이블에서주어진 조건을 만족하는 행을 찾는다.
  2. 선행 테이블의 조인 키 값을 가지고 후행 테이블에서 조인을 수행한다.
  3. 선행 테이블의 조건을 만족하는 모든 행에 대해 반복 작업을 수행한다.

👉 만약 선행 테이블에 사용가능한 인덱스가 존재한다면, 인덱스를 통해 선행 테이블을 액세스할 수 있다.
👉 조인을 성공하면 바로 조인 결과를 사용자에게 보여줄 수 있으므로 결과를 가능한 빨리 화면에 보여줘야하는 온라인 프로그램에 적당한 조인 기법이다.

3) Sort Merge 조인

👉 조인 칼럼을 기준으로 데이터를 정렬하여 조인을 수행한다.
👉 주로 스캔 방식으로 데이터를 읽는다.
👉 랜덤 액세스로 NL 조인에서 부담이 되던 넓은 범위의 데이터를 처리할 때 이용되는 조인 기법이다.
👉 정렬할 데이터가 많아 메모리에서 모든 정렬 수행 작업을 수행하기 어려운 경우에는 임시 영역(디스크)를 사용하기 때문에 성능이 떨어질 수 있다.
👉 일반적으로 대량의 조인 작업에서 정렬작업을 필요로 하는 Sort Merge 조인보다는 CPU 작업위주로 처리하는 Hash 조인이 성능상 유리하다. 하지만, Sort Merge 조인은 동등 조인뿐만 아니라 비동등 조인에서도 조인 가능하다는 장점이 있다.

  • 조인 작업 방법
  1. 독립적으로 각각의 테이블에서 데이터를 추출할 수 있다.
  2. 독립적으로 각각의 테이블에서 조인키를 기준으로 정렬 작업을 수행한다.
  3. 정렬 결과를 이용하여 조인을 수행하며 조인에 성공하면 추출 버퍼에 넣는다.

👉 조인 칼럼의 인덱스를 사용하지 않기 때문에 조인 칼럼의 인덱스가 존재하지 않을 경우에도 사용할 수 있다.

4) Hash 조인

👉 해슁 기법을 이용하여 조인을 수행한다.
👉 조인을 수행할 테이블의 조인 칼럼을 기준으로 해쉬 함수를 수행하여 서로 동일한 해쉬 값을 갖는 것들 사이에서 실제 값이 같은지 비교하면서 조인을 수행한다.
👉 조인 칼럼의 인덱스를 사용하지 않기 때문에 조인 칼럼의 인덱스가 존재하지 않을 경우에도 사용할 수 있다.
👉 '='으로 수행하는 동등 조인에서만 사용할 수 있다.
👉 결과 행의 수가 적은 테이블을 선행 테이블로 사용하는 것이 좋다.
👉 NL Join의 랜덤 액세스 문제와 SMJ의 정렬 작업 부담을 해결하기 위한 대안으로 등장하였다.

  • 조인 작업 방법
  1. 선행 테이블의 조인 키를 기준으로 해쉬 함수를 적용하여 모든 데이터에 대해 해쉬 테이블을 생성한다.
    (조인 칼럼과 select절에서 필요로 하는 칼럼도 함께 저장)
  2. 후행 테이블의 조인 키를 기준으로 해쉬 함수를 적용하여 해당 버킷을 찾는다.
    (3. 조인 키를 이용해서 실제 조인될 데이터를 찾는다. 조인에 성공하면 추출 버퍼에 넣는다.)
  3. 후행 테이블의 조건을 만족하는 모든 행에 대해서 반복 수행한다.

👉 해쉬 조인에서는 선행 테이블을 이용하여 먼저 해쉬 테이블을 생성한다고 해서 선행 테이블을 Build input이라고도 하며, 후행 테이블은 만들어진 해쉬테이블에 대해 해쉬 값의 존재를 검사 작업을 한다고 해서 Prove input이라고도 한다.


📄출처 : 덕성여대 wiset SQLD 자격증 특강 강의자료

좋은 웹페이지 즐겨찾기