4.1.1 NL 조인 기본메커니즘
-. 일반적으로 NL조인은 Outer와 Innser 양쪽 테이블 모두 인덱스를 이용한다.
-. 프로그래밍에서 Nested Loop 구조랑 같음
4.1.2 NL 조인 샐행계회 제어
-. use_nl 힌트 사용
-. B와C와 조인할때는 NL조인, D와 조인할떄는 Hash조인
4.1.3 NL 조인 수행 과정 분석
-. 조인 query 문 실행 순서 예시
4.1.4 NL 조인 튜닝 포인트
-, 사원X_1인덱스를 읽고 사원 table에서 엑세스 필터링하는 비율이 높다면 필터링 되는 컬럼을 사원_X1에 추가하는방안 고려
-. 고객_X1인덱스를 일고 고객 table에서 엑세스 필터링하는 비율이 높다명 필터링 되는 컬럼을 고객_X1에 추가하는 방안 고려
-. 맨 처음 액세스 하는 사원_X1 인덱스에서 얻은 결과 건수에 의해 전체 일량이 좌우된다.
4.1.5 NL 조인 특징 요약
-. 대량 데이터 조인할 때, NL조인 불리
-. 조인을 한 레코드씩 순차적으로 진행해서 부분처리가 가능하다.
4.1.7 NL 조인 확장 메커니즘
-. 배치 I/O 기능 '테이블 Prefetch' : 인덱스를 이용해 테이블을 엑세스하다가 디스크 I/O가 필요해지면, 이어서 곧 읽게 될 블록까지 미리 읽어서 버퍼캐시에 적재하는 기능.
-. Hint : nlj_prefetch, no_nlj_prefecth
-. 11g부터는 배치 I/O 메커니즘이 작동하기 때문에 일정 순서로 출력되기를 원한다면 no_nlj_batching(b) 힌트를 추가하거나 맨 바깥쪽에 ORDER BY 절에 정렬 기준을 명시해야한다.
※ 문제
-. 문제점 1. insert 테이블 alias를 왼쪽에 기술하는 습관이 좋다. 2. 인덱스 구성을 outer table로 만들었는데, inner table에서 row를 access하는 양이 많다면 성능에 문제를 야기한다. 그래서 inner table index구성에 신경써야한다. |
4.2 소트 머지 조인
4.2.1 SGA vs PGA
-. SGA 1. 캐시된 데이터는 어러 프로세스가 공유할 수 있다. 2. 여러프로세스가 공유할 수 있지만 동시에 엑세스할 수는 없다. 3. 동시에 엑세스하려면 프로세스 간 액세스를 직력화하기 위한 Lock 메커니즘으로 래치(Latch)가 존재한다. 4. 데이터 블록과 인덱스 블록을 캐싱하는 DB버퍼캐시는 SGA의 가장 핵심적인 구성요소이며, 여기서 블록을 읽으려면 버퍼 Lock도 얻어야한다. |
-. PGA 1. 오라클 서버 프로세스는 SGA에 공유된 데이터를 읽고 쓰면서, 동시에 자신만의 고유 메모리 영역을 갖는다. 2. 각 오라클 서버 프로세스에 할당된 메모리 영역을 PGA(Process/Program/Private Global Area)라고 부른며 프로세스에 종속적인 고유 데이터를 저장하는 용도로 사용한다. 3. 할당 받은 PGA공간이 작아 데이터를 모두 저장할 수 없을 때는 Temp 테이블스페이스를 이용한다. 4. PGA는 다른 프로세스와 공유하지 않는 독립적은 메모리 공간이므로 래치 메커니즘을 불필요하다. 5. 같은 양의 데이트를 읽더라도 SGA 버퍼캐시에서 읽을 때보다 훨씬 빠르다. |
4.2.2 기본 메커니즘
-. 1. 소트 단계 : 양쪽 집합을 조인 컬럼 기준으로 정렬한다.
-. 2. 머지 단계 : 정렬한 양쪽 집합을 서로 머지(Merge) 한다.
-. Hint : use_merge(c)
-. Sort Area(PGA를 이용하고 모자르면 temp tablespace이용-> 독립적인 공간이다. 즉 데이터를 읽을때 래치 획득 과정이 없다.)에 저장된 데이터 자체가 인덱스 역할을 하므로 소트 머지 조인은 조인 컬럼에 인덱스가 없어도 사용할 수 있는 조인 방식이다.
-. 조인 컬럼에 인덱스가 있어도 NL조인은 대량 데이터 조인할 때 불리하므로 소트 머지 조인을 사용할 수 있다.
4.2.4 소트 머지 조인의 주용도
-. 소트 머지 조인 대신 대부분 해시 조인을 쓴다. (더 빠르다.)
-. 하지만 해시조인은 등치(=) 조건이 아닐 떄 사용할 수 없기 때문에 아래 조건일 때 주로 사용한다.
* 조인 조건이 등치(=) 조건이 아닌 대량 데이터 조인
* 조인 조건식이 아예 없는 조인(Cross Join, 카테시안곱)
4.3 해시조인
4.3.1 기본 메커니즘
1. Build 단계 : 작은 쪽 테이블 (Build Input)을 읽어 해시테이블(해시 맵)을 생성한다. 2. Probe 단계 : 큰 쪽 테이블 (Probe Input)을 읽어 해시 테이블을 탐색하면서 조인한다. 3. Hint : use_hash(c) 4. 해시 테이블은 PGA 영역에 할당된 Hash Area에 저장된다. 너무크면 Temp tablespace 이용 |
4.3.2 해시 조인이 빠른 이유
-. PGA 영역을 할당하기 때문에, 래치 획득 과정이 없이 빠르게 데이터 탐색 및 조회
-. 해시 조인도 Build Input, Probe Input 각 테이블을 읽을 떄는 DB버퍼캐시를 공유한다.
4.3.3 대용량 Build Input 처리
-. 두 테이블 둘다 대용량이여서 인메모리 해시 조인이 불가능한 상황일때
-. DBMS는 분할/정복(Drive & Conquer) 방식을 사용한다.
4.3.4 해시 조인 실행계획 제어
-. 옵티마니저는 일반적으로 카디널리티(테이블 전체가 아닌 각 테이블 조건절에 대한 카니널리티)가 작은 테이블을 Build Input으로 선택한다.
-. 위와 같이 Plan이 풀렸다고 했을 때, T1(Build Input),T2 Hash join한 결과를 Build Input으로 쓰기 싫다면 no_swap_join_inputs(T2) 하면된다.
4.3.5 조인 메서드 선택 기준 ★★★★★
1. 소량 데이터 조인 -> NL조인 2. 대량 데이터 조인 -> 해시 조인 3. 대량 데이터 조인인데 해시 조인으로 처리 할 수 없을 때, 즉 조인 조건식이 등치(=) 조건이 아닐 때(조인 조건식이 아예 없는 카테시안 곱 포함) -> 소트머지조인 |
-. 같은 성능이라면 해시보다는 NL을 선택하는게 OLTP성 DB에서는 좋다.
-. 해시조인은 CPU와 메모리 사용이 크게 증가하고, 해시 맵을 만드는 과정에 여러가지 래치 경합도 발생한다.
1. 수행빈도가 낮고
2. 쿼리 수행 시간이 오래 걸리는 것
3. 대량 데이터 조회할 때로
로 정리할 수 있다.
4.4. 서브쿼리 조인
4.4.1 서브쿼리 변환이 필요한 이유
- 필터 오퍼레이션 : 서브 쿼리를 필터 방식으로 처리하는 방법
-- no_unnest : 서브쿼리를 쿼리 하나로 판단하여 최적하지 말고 그대로 수행하라는 의미의 힌트. 중첩 해제를 하지 마라.
SELECT
...
FROM 고객 c
WHERE c.가입일시 >= '19960101'
AND EXISTS (
SELECT /*+ no_unnest */
'x'
FROM 거래
WHERE 고객번호 = c.고객번호
AND 거래일시 >= '20210621'
)
- 필터 방식이란 마치 NL 조인처럼 동작하도록 하는 방식이다.
- 즉, 두 개의 쿼리를 각각 최적화하지 않고, NL 조인처럼 하나의 쿼리로 판단하여 최적화
- 실행계획은 아래와 같다.
-- 'FILTER'를 'NESTED LOOP'라고 생각하면 된다. FILTER TABLE ACCESS (BY INDEX ROWID) OF '고객' TABLE INDEX (RAGNE SCAN) OF '고객_X1' (INDEX) INDEX (RAGNE SCAN) OF '거래_X1' (INDEX)
- NL 조인과 차이점
- 서브 쿼리 조건절에 해당하는 값이 존재하면 조건절이 참이 true가 되었으므로 바로 break
- 서브 쿼리 조건절에 따른 결과를 캐싱한다. 동일한 조건의 결과가 올 경우 바로 캐시에서 찾을 수 있으므로 빠름.
- 항상 메인 쿼리가 Outer, 서브 쿼리가 Inner
- NL 조인과 차이점
- Unnesting 방식 : 메인과 서브 쿼리 간의 계층구조를 없애고, 서로 같은 레벨로 쿼리를 처리하는 방식이다. 위에서 설명한 필터 방식과 비교해서 이해하면 쉽다.
Filter 방식 Unnesting 방식 힌트 no_unnest unnest Outer와 Inner 쿼리 항상 서브 쿼리가 Inner 힌트를 이용해 선택 가능 조인 방식 NL 조인 방식 NL조인, 해시 조인 등 다양하게 가능 - unnest 힌트로 유도 가능
- 서브 쿼리가 메인 쿼리 보다 먼저 처리될 수 도 있다.
- 다양한 최적화 기법을 줄 수 있어 필터 방식보다 더 좋은 실행 경로를 찾을 수 있다.
- 서브 쿼리에 ROWNUM을 사용하면 unnest 힌트를 사용한다고 하여도 서브 쿼리를 최적화하지 않으니 ROWNUM을 서브 쿼리에 쓸 때 조심하자.
- 서브 쿼리 Pushing : 서브쿼리 필터링을 가능한 한 앞 단계에서 처리하도록 강제하는 기능
- 조건절에 존재하는 서브 쿼리에 해당하는 데이터가 적다면 서브 쿼리가 먼저 수행되는 것이 좋다. 하지만 필터 방식의 서브 쿼리는 대게 맨 마지막에 처리되므로, 이를 가장 먼저 처리되도록 강제하는 기능이다.
- push_subq 힌트로 유도
- 필터 방식에서만 사용 가능하기 때문에 no_unnest 힌트와 같이 힌트를 사용한다.
- 반대로, 가장 나중에 처리하게 하려면 no_push_subq를 사용하면 된다.
- 인라인 뷰 서브 쿼리와 조인
- 이 장 처음에 말했듯이 일반적으로 서브 쿼리가 포함된 쿼리는 옵티마이저는 각각의 쿼리를 나눠서 쿼리 변환을 하여 각각에 대해 더 나은 성능을 내도록 한다.
- 인라인 뷰도 옵티마이저가 독립적으로 최적화하기 때문에 merge 힌트를 사용하여 다르게 처리할 수 있다.
-- T에 대한 인라인 뷰 안에서는 조건에 해당하는 모든 데이터를 읽어야 한다. -- 모든 데이터를 읽고 결국엔 고객 테이블의 고객번호와 조인되는 값만 출력한다. -- 따라서 불필요한 데이터를 조회해서 비효율적이다. SELECT ... FROM 고객 C ,(SELECT ... FROM 거래 WHERE 거래일시 >= trunc(sysdate, 'mm') GROUP BY 고객번호) T WHERE C.가입일시 >= trunc(add_months(sysdate, -1), 'mm') AND T.고객번호 = C.고객번호 -- merge : 뷰를 메인 쿼리와 머지하라는 힌트이다. -- 머지를 하게되면 고객 테이블을 먼저 읽고 조인할 때는 해당 고객들에 대한 데이터만 읽는다. SELECT ... FROM 고객 C ,(SELECT /*+ merge */ ... FROM 거래 WHERE 거래일시 >= trunc(sysdate, 'mm') GROUP BY 고객번호) T WHERE C.가입일시 >= trunc(add_months(sysdate, -1), 'mm') AND T.고객번호 = C.고객번호
- 스칼라 서브 쿼리와 조인
- 스칼라 서브 쿼리는 NL 조인 방식으로 실행된다.
- 필터 서브 쿼리처럼 스칼라 서브 쿼리도 캐싱 기능을 사용하여 동일한 입력값에 대해서 캐싱해놓는다.
- 스칼라 서브 쿼리는 2개 이상의 항목을 조회할 수 없으므로 그럴 경우에는, 해당 쿼리를 인라인 뷰로 사용하는 편이 낫다.
- 스칼라 서브 쿼리도 unnest 사용이 가능하다
'Database > SQLP' 카테고리의 다른 글
[친절한SQL 6장] 기본 DML 튜닝 (0) | 2023.02.14 |
---|---|
[친절한 SQL 5장] 소트 튜닝 (0) | 2023.01.19 |
[친절한 SQL 3장] 인덱스 튜닝 (2) | 2022.12.26 |
[친절한 SQL 2장] 인덱스 기본 (0) | 2022.12.07 |
[친절한 SQL 1장] SQL 처리 과정과 I/O (0) | 2022.12.05 |