안녕하세요. NLJ_PREFETCH 조인중 DRIVING TABLE 인덱스를 잘 골라도 정렬이
발생하는 이유가 알고싶어 문의드리게 되었습니다..
혹시나 가르침이나 사견이라도 주시면 굉장히 감사하겠습니다.
**1 테스트 환경
CREATE TABLE T1(T1_COL1 NUMBER, T1_COL2 NUMBER,T1_COL3 NUMBER);
CREATE TABLE T2(T2_COL1 NUMBER, T2_COL2 NUMBER, T2_COL3 NUMBER);
CREATE INDEX IDX_T1 ON T1(T1_COL1,T1_COL2);
CREATE INDEX IDX_T2 ON T2(T2_COL1);
-------------------------------------------------------------------------------
** 2 일반적인 조인
SELECT /*+ LEADING(T1) NLJ_PREFETCH(T2) */ T1.*, T2.*
FROM T1, T2
WHERE T1.T1_COL1=1
AND T1.T1_COL2 = T2.T2_COL1
ORDER BY T1.T1_COL1, T1.T1_COL2;
-------------------------------------------------------------------------------
** 3 후행테이블 의도적 BATCH I/O 발생
SELECT /*+ LEADING(T1) NLJ_PREFETCH(T2) BATCH_TABLE_ACCESS_BY_ROWID(T2) */ T1.*, T2.*
FROM T1, T2
WHERE T1.T1_COL1=1
AND T1.T1_COL2 = T2.T2_COL1
ORDER BY T1.T1_COL1, T1.T1_COL2;
NL JOIN을 유도하면서 선행 테이블은 BATCH I/O가 발생을 막았는데
SORT가 발생하는 이유를 잘 모르겠습니다.
프리패치는 읽어올 가능성이 있는 테이블 블럭을 미리 메모리에 올리는 것입니다.
그다음 읽어야 할 블럭이 미리 올려둔 것이라면 그대로 읽으면 되고
그렇지 않다면 다시 메모리에 올려서 읽게 됩니다.
즉 메모리에 미리 올려둘 뿐 인덱스 스캔 방식은 동일합니다. -> 정렬 유지
배치테이블 엑세스는
위에서 언급한 실패(미리 올려둔 블럭이 지금 읽어야 할 블럭이 아닌 경우)한 경우에
이를 곧바로 메모리에 올리는게 아니고 쌓아 두었다가 나중에 한번에(배치) 올리는 방식입니다.
따라서 NLJ_PREFETCH 까지는 정렬 유지 기능이지만
BATCH_TABLE_ACCESS_BY_ROWID 가 추가되면 정렬 유지 안됩니다.
T1 을 읽어오는 순서가 틀어진게 아닙니다.
결과셋의 순서가 틀어지는 것이죠.
읽어오는 순서대로 조인하여 결과를 리턴하면 그대로 출력이 되는데
조인에 실패한 행들은 나중에 한꺼번에 다시 조인을 시도하게 되어서 출력 순서가 달라지는 것이죠.
예를 들어 T1 에서 1,2,3,4,5 읽어온 뒤
t2 를 조인하게 되면 1번을 찾으면서 인접 블럭들을 미리 메모리에 올리구요. (프리패치)
이때 올라온 메모리에 1,2,4,5 만 올라와 있다고 가정하면?
1, 2 는 성공하여 결과셋에 추가가 되고
3은 메모리에 올라온게 없어서 테이블 읽기를 일단 뒤로 미룹니다. (배치)
이 상태에서 4, 5 를 먼저 확인하는데 메모리에 올라와 있어서 결과셋에 추가가 되면
나중에 3을 메모리에 올려서 결과셋에 추가하게 되는 방식입니다.
출력 순서는 1,2,4,5,3 이 되겠지요.