옵티마이저가 SQL문을 가장 효율적으로 처리하기 위해 실행하는 계획을 선택하는 것을 의미한다.
📝FULL TABLE SCAN
테이블 전체 데이터를 읽어조건에 맞는 데이터를 추출하는 방식
📝ROWID SCAN
ROWID기준으로 데이터를 추출하는 방식으로 단일행 접근시 가장 빠르다
📝INDEX SCAN
원하는 데이터 추출을 위해 인덱스를 사용하여 검색하는 방식
📝INDEX SCAN이 무조건 좋은가?
FULL TABLE SCAN이 무조건 성능에 좋지 않고 INDEX SCAN이 성능에 좋다는 것이라는 생각은 잘못 된 것이다.
데이터가 많은 테이블로부터 일부의 데이터를 추출해야하는 INDEX SCAN이 유용한 건 사실이지만 반대로 테이블에 있는 대부분의 데이터를 추출해야하면 FULL TABLE SCAN이 더 효과적일 수 있다.
📝INDEX SCAN 종류
INDEX UNIQUE SCAN
UNIQUE INDEX를 이용하여 필요한 데이터 블록을 접근하는 방식
한건 이하의 ROWID를 반환하는 INDEX SCAN 방식으로 모든 컬럼의 조건절에 '='로 명시된 경우를 의미한다.
INDEX RANGE SCAN
인덱스를 이용하여 필요한 데이터 블록을 접근하는 방식 중 가장 일반적인 형태
한건 이상의 필요한 데이터가 포함된 일정 범위의 인덱스 블록을 오름차순으로 접근하는 방식
동일 INDEX KEY 값에 대해서는 ROWID 기준으로 오름차순 정렬
INDEX UNIQUE SCAN을 제외한 모든 INDEX SCAN에 사용, 대소비교(<.>)가 하나라도 들어간 경우 예) menu_price > 2000
와일드 카드 문자(%)가 조건 값 뒤에 존재하는 경우 예) like 'abc%'
INDEX RANGE SCAN DESCENDING
INDEX RANGE SCAN과 기본적인 접근 방식은 동일
오름차순이 아닌 내림차순으로 인덱스 블록을 접근하는 방식
INDEX RANGE SCAN을 수행함과 동시에 ORDER BY DESC절을 만족하는 경우
INDEX SKIP SCAN
결합 인덱스의 선행 컬럼을 건너뛰는 형태로 인덱스 블록에 순차적으로 접근
선두 칼럼의 고유 값의 개수가 적고후행 칼럼의 고유 값이 많을 때효과적입니다 예) 부서A, B, C가 있고 급여가 500만원 ~ 700만원인 사람을 추려내야할 때 인덱스를 (부서, 급여)로 만들어서 A부서에서 500만원 ~ 700만원까지 [인덱스라서 정렬된 상태] 찾은 후 그 앞이나 뒤는 안 찾고 B부서로 넘어가는 방식
INDEX FULL SCAN
인덱스 리프 블록 전체를 SCAN하는 방식
단일 블록 순차 접근으로 병렬 처리는 불가능하나 인덱스 키순 정렬이 보장된다.
모든 데이터를 가져오는데 조건절은 없이 정렬이 있는 경우에 해당한다 예) SELECT * FROM MEMBER ORDER BY MEMBER_ID
INDEX FAST FULL SCAN
인덱스 리프 블록 전체를 SCAN하는 방식
병렬 처리가 가능해 빠르나 인덱스 키 순으로 정렬은 보장 불가능
INDEX FAST FULL SCAN은 TABLE FULL SCAN의 INDEX VERSION이라고 생각하면 좋다.
HINT를 이용해 유도해야하는 걸로 알고 있음
📝옵티마이저가 FULL TABLE SCAN을 선택하게되는 이유
조건절에서 비교한 컬럼에 인덱스가 없는 경우
인덱스는 있으나 데이터가 테이블의 많은 양을 차지해 FULL TABLE SCAN의 비용이 INDEX SCAN보다 적다고 판단하는 경우
최적한 인덱스는 있으나테이블 데이터 자체가 적어 FULL TABLE SCAN 비용이 INDEX SCAN보다 적다고 판단하는 경우
📝실행 계획 테이블 (PLAN_TABLE)
ORACLE에서 실행 계획을 테이블에 저장하게 되는데 해당 테이블 이름은 PLAN_TABLE이다.
EXPLAIN PLAN -- EXPLAIN PLAN 선언부
SET STATMENT_ID = 'EP_TEST' -- SQL에 임의 ID 부여
FOR
SELECT E.empno, E.ename, D.deptname
FROM emp E, dept D
WHERE E.deptno=D.deptno;
해당 SQL은 SQL의 실행 계획을 저장하는 것일 뿐 실제로 SQL이 실행되는 것이 아니다.
이거는 옵션에 따라 직접 실행시키면서 SQL 실행 계획을 저장할 수도 있다.
PLAN_TABLE에서 DBMS_XPLAN 패키지를 이용해 조회가 가능하다
실행 계획 순서는 위에서 아래로 진행되며 가장 들여쓰기가 많이 된 곳에서 부터 시작되며 실행 순서는 위에서 아래로 진행된다.같은 수준에서 위에서 아래로 실행된 게 그 이후에 없으면 상위로 올라가며 반복된다.
해당 실행 계획의 순서는5 → 3 → 4 → 2 → 1 → 0 이다
📝실행계획 요소 (PLAN_TABLE)
STARTS
실행 계획 정보중 기본적인 최소한의 정보를 출력
A-ROWS
실제SQL이 실행된 후 수집되는 행 수
A-TIME
실제SQL수행에 소요된 시간
E-ROWS
SQL에 대한 예측되는 행의 수
E-BYTES
SQL실행 시 접근하는 데이터 바이트 수의 예측값
USED_MEM
최근 실행 시 실제 메모리 사용한 양
그외 더 다양한 정보가 있습니다.
📝SQL TRACE
실행 계획은 물론 여러 세션에서 수행한 SQL 통계 정보 수행 시간, 결과 등을 TRACE로 기록하여 파일 형태로 저장하는 방법이다. 이걸 이용해 필요한 부분의 SQL문의 실행 정보를 수집할 수도 있다.
최소한의 CPU I/O 메모리를 사용해 최대한 빠른 시간내 원하는 데이터 작업을 수행시키는 것 아무리 쿼리의 성능을 튜닝해도 근본적인 스키마 구조가 문제라면 밑빠진 독에 물 붓기이기 때문에 이런 경우 아예 근본적인 스키마 구조부터 뜯어고쳐야할 수 있다. 데이터 모델링을 할 때는 일반적으로 사용자의 이용 빈도(자주 사용하는 화면)를 고려하며 설계 하는 게 좋다
📝옵티마이저
DBMS의 두뇌라고 표현할 수 있다. SQL이 들어오면 문법 에러 및SQL문을 처리하는 최적의 방법을 도출해서 실행하게 한다.
📝SQL 실행 순서
문법 오류 검사
실행한 적이 있는 SQL문인지 메모리(SHARED POOL)에서 체크 후 실행
실행한 적 있으면기존 방식으로 실행(SOFT PARSING) →
실행한 적 없으면 처리 방식에 대한 실행 계획 수립(HARD PARSING) 그 이후 SHARED POOL에 실행 계획 저장
📝옵티마이저 종류
규칙 기반 옵티마이저
특정 규칙을 기반으로 실행 계획 수립하고 우선순위가 높은 걸 우선시한다.
ROWID에 의한 단일행 실행
클러스터 조인에 의한 단일행 실행
AHSH CLUSTER KEY에 의한 단일행 실행
UNIQUE KEY 또는 PRIMARY KEY에 의한 단일행 실행
클러스터 조인
HASH CLUSTER KEY
INDEXED CLUSTER KEY
결합 인덱스
단일 컬럼 인덱스
인덱스에 의한 컬럼 BOUNDED RANGE
인덱스에 의한 컬럼의 UNBOUNDED RANGE
SORT MERGE JOIN
인덱스로 구성된 컬럼의 MAX 또는 MIN 처리
인덱스로 구성된 컬럼의 ORDER BY
FULL TABLE SCAN
예를 들어 아래와 같은 쿼리문이 존재할 때 emp_no가 PRIMARY KEY 설정(인덱스)가 안 되어있는 경우15번의 FULL TABLE SCAN 방식으로 실행될 것이고 있는 경우는 4번의 방식으로 실행되게 된다.
SELET E.e_name
FROM emp E
WHERE E.emp_no = '12345';
우선 순위가 정해져있기 때문에 미리 예측을 할 수 있다는 장점이 있다. 그렇기 때문에 SQL을 통제하는 것이 가능하다
하지만 이러한 규칙이 항상 유리하지 않다 성별인 남자이고 입사일이 2013년 1월 1일 ~ 2013년 1월 3일인 사람을 검색하는 SQL을 작성하고자할 때 아래 SQL과 같이 작성하는 경우 성별인 남자인 직원을 걸러내는 인덱스를 사용하는 것보다 3일 사이에 입사한 직원을 걸러내는게 더 효율적이다. → 남자인 사람에서 입사일을 걸러내는 것보다 입사일에서 남자인 사람을 걸러내는게 불필요한 데이터를 많이 읽게 되는 걸 줄일 수 있다
SELECT E.e_name
FROM emp E
WHERE E.gender = '남자'
AND E.hiredate BETWEEN '20130101' AND '20130103'
위와 같은 쿼리를 좀더 효율적으로 하기 위해 인덱스를 안 타게끔 아래와 같이 조정이 가능하긴 하다.
SELECT E.e_name
FROM emp E
WHERE E.gender || '' = '남자'
AND E.hiredate BETWEEN '20130101' AND '20130103'
또한 규칙기반 옵티마이저에서는 이러한 단점들이 존재한다.
힌트를 사용할 수 없다
HASH JOIN을 사용 할 수 없다
거의 사용하지 않는 추세이며 ORACLE의 경우 10g 이후 공식적으로 지원하지 않는다.
비용 기반 옵티마이저
최소 비용으로 처리할 수 있는 실행 계획 수립, 가장 많이 쓰이는 형식의 옵티마이저
하나의 SQL을 받으면 최대 2,000개의 실행 계획을 만들고 가장 적은 비용이 드는 실행계획을 사용자에게 제시한다.
PARSING 과정을 거친 SQL은 PARSING 트리 형태로 변형되어 Query Transformer는 넘겨 받은 SQL을 보고 같은 결과를 도출하되 좀 더 나은 실행 계획을 갖는 SQL로 변형이 가능한지 판단해 변환 작업을 수행 예를 들면 복잡한 서브쿼리나 뷰를 사용한 SQL을 일반적인 조인 형태의 SQL로 변환해 실행 계획을 도출하기 좋은 상태로 만든다.
Estimator
Query Transformer를 통해 변환 작업을 마치고난 SQL은 Estimator로 넘겨지게 되는데 이때 수행하는데 드는 총 비용을 계산한다.
Plan Generator
Estimator를 통해 계산된 값들을 토대로 후보군 실행계획 도출 Row Source Generator를 통해 출력이 가능한 코드 형태로 바꾼다.
📝FIRST_ROWS_n
비용기반 옵티마이저는 CHOOSE, FIRST_ROWS와 같은 모드도 존재하지만 현재는 사용하지 않고 DBMS에서도 FIRST_ROWS_n을 권장하며 실행 결과를 출력하는데 걸리는 응답 속도를 최적화 하는게 목표이다.
📝ALL_ROWS
SQL 실행 결과 전체를 빠르게 처리하는데 최적화된 실행 계획을 세우는 것입니다. ORACLE의 경우 10g 이후에 이값이 기본적으로 설정되어있다.
📝옵티마이저 이용 통계정보
옵티마이저의 경우 통계 정보를 따라 자동적으로 최적의 실행계획을 세운다 이용하는 통계정보는 아래와 같다
테이블
테이블 전체 행의 수
테이블이 차지하고 있는 전체 블록 수
테이블의 행들이 갖는 평균 길이 등
컬럼 컬럼 값의 종류
컬럼 내 NULL 값의 분포도
컬럼 값의 평균 길이
컬럼 내 데이터 분포의 추정치 등
인덱스
LEAF BLOCK 수 : 데이터 보관하는 블록 수
LEVELS : 인덱스 트리의 LEVELS 정보
CLUSTERING FACTOR : 접근하고자 하는 데이터가 모여 있는 밀집도
시스템
I/O 성능 및 사용률
CPU 성능 및 사용률 등
시스템 운영중 DBMS 전체의 옵티마이저 모드를 변경하는 작업은 가급적 하지 않는 것이 좋다.
📝옵티마이저 한계
잘못된 비용 계산의 한계
복잡한 쿼리문때문에 정확하지 않은 통계자료 발생
동시성 배제한 비용 계산의 한계
비용 예측시 한 개의 SQL만 실행된다는 전제로 진행하게 되는데 실제 운영 환경에서 같은 블록을 접근하는 등 상황이 발생해 대기상태가 될 수 있다.
컬럼 타입에 대한 비용 에측의 한계
날짜 유형의 데이터를 날짜 데이터 타입이 아닌 문자 데이터 타입을 컬럼으로 사용할 경우 비용 계산 방식이 달라져 예측이 어려워질 수 있다 예를 들면 날짜 데이터를 DATE타입으로 선언하면 특정 월에는 1부터 최대 31까지의 날짜만 존재하지만 YYYYMMDD 형식을 갖는 VARCHAR 타입의 데이터를 선언하면 이는 연속된 문자열이기 때문에 31일 이후의 날짜가 존재할 수 있다고 판단한다. 그 결과 예측되는 최종비용 또한 정확성이 떨어지게 된다.
, A.COMPANY AS COMPANY -- COMPANY , A.WRITER AS WRITER -- 작성자 FROM COVID_VIEW A WHERE A.ID = '1' OR A.ID = '2' AND A.REG_DATE > 20220104 GROUP BY A.COMPANY ORDER BY A.REG_DATE DESC
SELECT 문 끝나는 줄에 줄맞추기
대문자로 전부 작성하기 → 테이블명이 소문자인경우 대문자 사용 못할 경우 소문자로 다 통일
-- 뷰 테이블 생성
CREATE VIEW 뷰이름 AS
SELECT 필드이름1, 필드이름2, ...
FROM 테이블이름
WHERE 조건
-- 뷰 테이블 삭제
DROP VIEW [table name]
-- 뷰 테이블 생성 및 수정
CREATE OR REPLACE VIEW 뷰이름 AS
SELECT 필드이름1, 필드이름2, ...
FROM 테이블이름
WHERE 조건
LOAD DATA LOCAL INFILE '파일 경로' INTO TABLE [데이터를 넣을 테이블명] FIELDS TERMINATED BY "열구분자" LINES TERMINATED BY '행구분자';
예)
LOAD DATA LOCAL INFILE 'D:\covid.csv' INTO TABLE covid_org FIELDS TERMINATED BY "," ENCLOSED BY '\"' LINES TERMINATED BY '\r\n';
D:\covid.csv 파일을 covid_org 테이블에 insert 하는데 열 구분자는 , 이고 내용에 ,가 들어간 경우 ENCLOSE BY를 통해 내용에 ,를 예외처리해준다.
행 구분자는 \r\n이다. (내용에 , 가 없는 경우 ENCLOSED BY를 제외해도된다)
이 쿼리문으로 CSV 파일로 대량 데이터 넣을 수 있습니다.
CREAT TABLE [테이블명] ( SELECT * FROM [복사할 테이블명])
이 쿼리문으로 기존 테이블의 내용을 카피하거나 특정 필드를 뽑아내거나 JOIN등으로 한번에 넣을 수 있습니다.