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

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

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


출처 : https://docs.oracle.com/cd/B10500_01/server.920/a96524/c04space.htm
Direct Path Read 방식으로 읽는 경우를 제외하면 모든 블록은 DB 버퍼캐시를 경유해서 읽는다.

Single Block I/O : 한 번에 한 블록씩 요청해서 메모리에 적재하는 방식, 인덱스를 이용할 때 기본적으로 사용
Multiblock I/O : 한 번에 여러 블록씩 요청해서 메모리에 적재하는 방식, 테이블 전체를 스캔할 때 사용
Direct Path I/O를 제외한 모든 블록 I/O는 메모리 버퍼캐시를 경유
Latches : 멀티스레드 환경에서 데이터의 일관성을 유지하고, 동시성 문제를 방지하기 위해 사용되는 경량 잠금 메커니즘
Hash buckets : 해시 테이블의 구성 요소로, 특정 키 값을 가진 데이터를 저장하는 공간
Buffer headers : 데이터베이스 버퍼 캐시에서 각 데이터 블록에 대한 메타데이터를 저장하는 구조, 블록의 상태 및 위치 정보를 포함

자식 노드중 가장 왼쪽 끝에 위치한 블록 리프 블록에 저장된 각 레코드는 키값 순으로 정렬 테이블 레코드를 가르키는 주소값, RoWID를 갖으며 인덱스 키값이 같으면 ROWID 순으로 정렬
- ROWID = 데이터 블록 주소 + 로우 번호
- 데이터 블록 주소 = 데이터 파일 번호 + 블록 번호
- 블록 번호 : 데이터파일 내에서 부여한 상대적 순번
- 로우 번호 : 블록 내 순번
이동한 브랜치 블록에는 찾고자 하는 값과 정확히 일치하는 레코드가 있으면 하위 블록으로 이동하면 안되고 바로 직전 레코드를 가르키는 하위 블록으로 이동
수직적 탐색을 통해 스캔 시작점을 찾았으면 수평적으로 리프 블록을 스캔한다. 인덱스를 정상적으로 사용한다는것은 리프 블록에서 스캔 시작점을 찾아 거기서부터 스캔하다가 중간에 멈추는 것 인덱스 선두 컬럼이 가공되지 않은 상태로 조건절에 있으면 인덱스 Range Scan이 가능
인덱스를 스캔하는 이유는 검색 조건을 만족하는 소량의 데이터를 인덱스에서 빨리 찾고 테이블 레코드를 찾아가기 위한 주소값 즉 ROWID를 얻기 위함(논리적 주소)
I/O 성능을 높이려면 버퍼캐시를 활용, 디스크로 가기 전에 버퍼캐시부터 검색, 못 찾을 떄만 디스크에서 블록을 검색
루트 블록에는 키 값을 갖지 않는 특별한 레코드를 의미하며 가장 왼쪽에 있다고 해서 키 값을 가진 첫 번째 레코드

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

출처 : http://www.gurubee.net/wiki/pages/31752196
인덱스 스캔 효율서은 인덱스 컬럼을 조건절에 모두 등치(=) 조건으로 사용할 떄 가장 좋다.

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

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

# table full scan
select * from 거래
where(:cust_id is null or 고객ID = :cust_id)
and 거래일자 between :dt1 and :dt2
--- 인덱스 : 상품명 + 상품분류코드
select * from 상품
where 상품명 = :prd_nm
and 상품분류코드 like :prd_cls_cl || '%'
- 인덱스 선두 컬럼에 대한 옵션 조건을 LIKE/BETWEEN 연산자로 처리 x
- NULL 허용 컬럼에 대한 옵션 조건을 LIKE/BETWEEN 연산자로 처리 x
- 숫자형이면서 인덱스 액세스 조건으로도 사용 가능한 컬럼에 대한 옵션 조건 처리 LIKE 방식 x
- LIKE를 옵션 조건에 사용시 컬럼 값 길이가 고정적
- 조건절에 항상 사용하거나, 자주 사용하는 컬럼을 선정
- '=' 조건으로 자주 조회하는 컬럼을 앞쪽에 배치
Nested Loop Join으로 한 테이블에서 다른 테이블의 모든 행을 탐색
테이블에서 하나의 행을 선택하는 Outer Loop, 조인 조건에 맞는 모든 행을 순회하는 Inner Loop
Inner 쪽 테이블은 인덱스를 사용해야 한다. Inner 루프에서 인덱스를 사용하지 않으면 Table Full Scan을 반복한다.

출처 : https://www.interdb.jp/pg/pgsql03/05/01.html
PGA는 다른 프로세스와 공유하지 않는 독립적인 메모리 공간이므로 래치 메커니즘이 불필요 따라서 같은 양의 데이터를 읽어도 SGA 버퍼캐시에서 읽을 때 보다 훨씬 빠름
| 구분 | SGA (System Global Area) | PGA (Program Global Area) |
|---|---|---|
| 역할 | 인스턴스 전체에서 공유되는 메모리 영역 | 개별 사용자 세션에서 사용되는 비공유 메모리 영역 |
| 구성 요소 | 데이터 버퍼 캐시, 공유 풀, 대기열, 로그 버퍼 등 | 정렬 영역, 해시 영역 등 |
| 관리 주체 | 데이터베이스 인스턴스 | 서버 프로세스 또는 백그라운드 프로세스 |
| 공유 여부 | 여러 사용자 세션 간 공유 | 각 세션 별로 독립적 |
| 주요 기능 | 데이터 및 SQL 문 캐싱, 데이터베이스 동시성 제어 지원 | 세션별 SQL 작업 처리, 정렬 작업, 해시 조인 처리 |

출처 : https://logicalread.com/oracle-11g-sort-merge-joins-mc02/
쿼리 변환은 옵티마이저가 SQL을 분석해 의미적으로 동일하면서도 더 나은 성능이 기대되는 형태로 작성하는 것
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;
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);
SELECT employee_id, name, salary,
salary - (SELECT AVG(salary) FROM employees) AS salary_difference
FROM employees;
메모리 공간인 Sort Area가 다 차면, 디스크 Temp 테이블스페이스를 활용
데이터파일과 컨트롤 파일에 가해지는 모든 변경사항을 Redo 로그에 기록하는데 트랜잭션 데이터가 어떤 이유에서건 유실됐을 때, 트랜잭션을 재현함으로써 유실 이전 상태로 복구하는 데 사용
DML을 수행할 때마다 Redo 로그를 생성해야 하므로 Redo 로깅은 DML 성능에 영향을 미치기에 INSERT 작업에 대해 Redo 로깅 생략 기능을 제공하는 이유
과거에는 Rollback이라는 용어를 주로 사용했지만, 오라클은 Undo라는 용어를 사용
Undo는 트랜잭션을 롤백함으로써 현재를 과거 상태로 되돌리는데 사용, 변경된 블록을 이전 상태로 되돌리는 데 필요한 정보를 로깅

출처 : https://databaseinternalmechanism.com/oracle-database-internals/oracle-process-architecture/
일반적인 블록 I/O는 DB 버퍼캐시를 경유하는데 읽고자 하는 블록을 먼저 버퍼캐시에서 찾아보고, 찾지 못할 때만 디스크에서 읽고 데이터를 변경할 때도 먼저 블록을 버퍼캐시에서 찾고 찾은 버퍼블록에 변경을 가하고 나면, DBWR 프로세스가 변경된 블록(Dirty 블록)들을 주기적으로 찾아 데이터파일에 반영
블록을 재사용할 가능성이 있느냐도 중요한데, Full Scan 위주로 가끔 수행되는 대용량 처리 프록램이 읽어 들인 데이터는 대개 재사용이 낮음
Insert에 대한 로우 Lock 경합은 Unique 인덱스가 있을 떄만 발생
MVCC 모델을 사용하지 않는 DBMS는 SELECT 문에 공유 Lock를 사용 두 트랜잭션이 같이 Lock를 설정할 수 있는 의미 이며 공유 Lock, 배타 Lock은 호환되지 않기에 DML과 SELECT가 서로 진행을 방해할 수 있음
테이블 Lock라고 하면, 테이블 전체를 Lock으로 생각하지만 자신이 해당 테이블에서 현재 어떤 작업을 수행 중인지를 알리는 일종의 Flag
어떤 모드를 사용했는지에 따라 후행 트랜잭션이 수행할 수 있는 작업의 범위가 결정
사용자들이 같은 데이터를 동시에 수정할 것으로 가정하며 한 사용자가 데이터를 읽는 시점에 Lock을 걸고 조회 또는 갱신 처리가 완료될 대까지 유지
사용자들이 같은 데이터를 동시에 수정하지 않을 것으로 가정 데이터를 읽을 때 Lock을 설정하지 않음
각 테이블의 식별자의 단일컬럼 일련번호 또는 구분 속성별 순번을 채번하기 위해 별도 테이블을 관리하는 방식
직렬화가 이루어지므로 두 트랜잭션이 중복 값을 채번할 가능성을 원천적으로 방지
INSERT 과정에 중복 레코드 발생에 대비한 예외처리에 크게 신경쓰지 않아도 되며 성능이 빠름
참고자료 : 친절한 SQL 튜닝