데이터베이스2025. 3. 7. 11:36

3과목. SQL 고급활용 및 튜닝
1. SQL 수행 구조

2. SQL 분석 도구

3. 인덱스 튜닝

4. 조인 튜닝

5. SQL 옵티마이저

6. 고급 SQL 튜닝

7. Lock과 트랜잭션 동시성 제어

 

 

 

 

Lock
- 같은 자원을 액세스 하는 다중 트랜잭션 환경에서 데이터베이스의 일관성과 무결성을 유지하면서 순차적인 진행을 보장하는 직렬화 수단

공유 Lock (Shared Lock)
- 데이터를 읽기 위해 사용하는 Lock
- 한 자원에 대해 같은 공유 Lock 끼리는 여러개 설정이 가능하다. (동시에 읽기 가능)
- 배타적 Lock 과는 동시 진행 불가.

배타적 Lock (Exclusive Lock)
- 데이터를 변경하고자 할 때 사용하는 Lock
- 트랜잭션이 종료될때까지 유지되며, 다른 공유 Lock / 배타적 Lock 이 동시에 설정 불가하다.

블로킹 (Blocking)
- Lock 경합으로, 다른 트랜잭션에서 Lock 이 걸려서, 대기하고 있는 상태

교착상태 (Deadlock)
- 두개의 세션이 각각 Lock 을 설정한 리소스를 서로 액세스 하려고 블로킹 상태에 있는 상태



SET TRANSACTION ISOLATION LEVEL
    { READ UNCOMMITTED
    | READ COMMITTED
    | REPEATABLE READ
    | SNAPSHOT
    | SERIALIZABLE
    }

READ UNCOMMITTED
- 다른 트랜잭션에 의해 수정되었지만 아직 커밋되지 않은 행을 문이 읽을 수 있도록 지정

READ COMMITTED
- 다른 트랜잭션에 의해 수정되었지만 아직 커밋되지 않은 데이터를 문이 읽을 수 없도록 지정
- MS-SQL Select : Row 레코드를 읽는 동시에 Lock 을 획득하고, 다음 레코드로 이동하는 순간 Lock 이 해제된다.

REPEATABLE READ
- 다른 트랜잭션에 의해 수정되었지만 아직 커밋되지 않은 데이터를 문이 읽을 수 없도록 지정하고 현재 트랜잭션이 완료될 때까지 현재 트랜잭션이 읽은 데이터를 다른 트랜잭션이 수정할 수 없도록 지정
- MS-SQL Select : Row 레코드를 읽는 동시에 Lock 을 획득하고, 최종커밋 또는 롤백 하는 순간 Lock 이 해제된다.

SNAPSHOT
- 트랜잭션의 문이 읽은 데이터가 트랜잭션별로 트랜잭션을 시작할 때 존재한 데이터 버전과 일관성이 유지되도록 지정

SERIALIZABLE
- 가장 엄격한 격리수준. 트랜잭션이 완료될 때까지 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정 및 입력이 불가능


트랜잭션의 4가지 특징 (ACID)

 

원자성 (Atomicity) 분해가 불가능한 업무의 최소단위로, 전부 처리되거나, 전부 처리되지 않아야 한다.
일관성 (Consistency) 트랜잭션 결과로 데이터베이스 상태가 모순되지 않아야 한다.
격리성 (Isolation) 트랜잭션의 중간 값에 다른 트랜잭션이 접근할 수 없다.
영속성 (Durability) 트랜잭션의 결과가 데이터베이스에 영속적으로 저장된다.



Lock Escalation
- 로우 레벨 Lock 이 페이지, 익스텐트, 테이블 레벨 Lock 으로 점점 확장되는 것.
- 오라클은 로우 속성으로 Lock 을 관리하므로, Lock Escalation 이 발생하지 않는다.



동시성
- DML 수행 시, ROW 단위로 배타적 Lock 이 걸린다.

 


오라클
- DML 문이 실행되는 동안 해당 ROW 는 update/delete 불가하다. select 는 가능.

 

MS-SQL
- DML 문이 실행되는 동안 해당 ROW 는 select/update/delete 불가하다.
- 제약조건이나 unique 인덱스 설정이 없으면, insert 하는 동안에 다른 insert 도 가능하다.
- insert 하는 동안에, 다른 select 절이 full scan 하게 되면, select 도 동시에 실행이 불가능하다. => index range 스캔을 하게 되면 select 가능.


TX Lock (Transaction Lock)
- 행 수준(Row-level) 잠금

TM Lock(Table Lock)
- 테이블 잠금, 테이블에 대한 구조적 변경(DDL) 이나, DML(데이터 조작 언어) 작업 시 데이터 무결성을 보장
- SELECT for UPDATE 문 에서도 TM Lock 이 설정된다.


이상현상

Dirty Read
- 다른 트랜잭션이 변경중인 데이터를 읽어서 사용했는데. 다른 트랜잭션이 rollback 되는 경우.

Non-Repeatable Read
- 한 트랜잭션 내에서 값을 두번이상 읽어서 사용하는 중간에 다른 트랜잭션이 해당값을 변경(update / delete) 해서, 값이 달라지는 경우.

Phantom Read
- 한 트랜잭션 내에서 일정 범위의 값을 두번이상 읽어서 사용하는 중간에 다른 트랜잭션이 새로운 값을 추가(insert) 함으로써, 처음 읽을때 없던 값이 나중에 생긴 경우.



비관적 동시성 제어
- 데이터를 읽는 시점부터 Lock 을 설정한다.

낙관적 동시성 제어
- 데이터를 읽는 시점에 Lock 을 설정하지 않는다.
- 다만, 읽은 데이터를 다시 읽거나, 수정하는 시점에 반드시 변경 여부를 확인한다.


SELECT FOR UPDATE
- 해당 행을 읽으면서, 하단에서 변경하기 위해 lock 을 건다.
- Lock 이 걸린 행을 만나면, Lock 이 해제될 때까지 무작정 기다린다.

SELECT FOR UPDATE WAIT n
- Lock 이 걸린 행을 만나면, n 초간 기다리고, 해제되지 않으면 SELECT 문 전체를 종료한다. (Exception)

SELECT FOR UPDATE NOWAIT
- Lock 이 걸린 행을 만나면, 즉시 SELECT 문 전체를 종료한다. (Exception)

snapshot too old 에러
- Table Full Scan 및 해시 조인을 사용하면 에러를 줄일 수 있다.
- 데이터 조회시 Order by 를 사용하면 에러를 줄일 수 있다.
- 튜닝을 통해 줄일수는 있어도 완전히 해소하기는 어렵다.
- Undo 영역의 크기를 증가시키고, commit 을 자주 수행하지 말아야 한다.


 

Posted by 헝개
데이터베이스2025. 3. 6. 13:03

SQLP : 제3과목은 SQL 고급활용 및 튜닝 이다.
 
 
3과목. SQL 고급활용 및 튜닝
1. SQL 수행 구조

2. SQL 분석 도구

3. 인덱스 튜닝

4. 조인 튜닝

5. SQL 옵티마이저

6. 고급 SQL 튜닝

7. Lock과 트랜잭션 동시성 제어

 

 

 

 

 

소트
- Sort 양이 많을때 중간 정렬된 집합을 Temp 영역에 저장해둔다. 이 집합을 Sort Runs 라고 한다.

Optimal 소트
- 정렬 대상 집합이 크지 않아 Sort Area 에서 정렬 작업을 완료하는 것

Onepass 소트
- 정렬  대상 집합을 디스크에 한번만 기록하는 것

Multipass 소트
- 정렬  대상 집합을 디스크에 여러번 기록하는 것


Sort 실행계획

SORT (ORDER BY) : Order By 절
SORT (GROUP BY) : Group By 절 + Order By 절
SORT (UNIQUE) : Group By 와 Unique 또는 Distinct 연산, Minus, Intersect, Union 연산
SORT (AGGREGATE) : Count, Avg, Sum, Min, Max 등의 연산
HASH (GROUP BY) : Group By 절

 



페이징처리 표준 패턴

SELECT *
FROM ( SELECT ROWNUM NO, A.*
FROM (
/* SQL BODY */
)
WHERE ROWNUM <= (:page * 10)
)
WHERE NO >= (:page-1) * 10 + 1
WHERE ROWNUM <= (:page * 10) 절에 의해 top n stopkey 알고리즘이 적용된다. 부분 범위처리 가능




First Row Min/Max
- min/max 연산을 사용할때, 조건절와 min/max 컬럼값이 모두 인덱스에 있어야 효율적이다.
- First Row Min/Max 이 안되는 경우, order by 와 rownum <= 절로 변경하는것이 효율적이다.

DML 성능에 영향을 미치는 요소
- 인덱스, 무결성제약, 조건절, 서브쿼리, redo/undo 로깅, Lock, 커밋

Array Processing기능
- 한 번의 SQL 수행으로 다량의 로우를 동시에 insert/update/delete
- 네트워크 부하를 줄이고, 데이터베이스 Call 횟수를 줄여준다.
- OLTP 시스템에서 성능 개선

Direct Path Insert
- 버퍼캐시를 거치지 않고, 작업 데이터파일에 기록
- Exclusive 모드 TM Lock 을 설정
- Freelist 를 참조하지 않고 HWM 바깥 영역에 데이터를 순차적으로 입력
- Undo 로깅을 최소화


alter table tablename nologging; // redo 로그를 생성하지 않음 : insert 에서만 작동
insert /*+ append */ into tablename // Direct Path Insert 작동
select * from sourcetable;





Recursive Call 은 Parse Call / Execute Call / Fetch Call 단계에서 모두 나타날 수 있다.
Array Size 를 늘리면, Fetch Call 을 줄일 수 있다.
Array Processing 을 늘리면, Execute Call 을 줄일 수 있다.

 




병렬처리 Insert

인덱스 비활성화 / 활성화
alter index PK_index_name on tablename DISABLE;
alter index PK_index_name on tablename ENABLE;

 


Merge Into 사용법

Merge Into 고객 c
Using 고객 d
On ( c.법정대리인_고객번호 = d.고객번호
and c.성인여부 = 'N' )
when matched then
-- update set c.법정대리인연락처 = d.연락처
when not matched then
-- insert (col1, col2) values (d.col1, d.col2)
;




대용량 데이터 UPDATE 튜닝
- update 구문을 truncate / insert 로 대체

update 주문 set 상태코드 = '9999'

=>

create table tmp as select * from 주문;
truncate table 주문;
insert into tmp select ..., '9999' as 상태코드, ... from tmp;
drop table tmp;





어플리케이션 커서를 캐싱 하면, Parse Call 이 1번만 나타난다.
Array Size 를 늘리면, Fetch Call 이 줄어든다 : fetch call * array size = rows
Array Processing 을 하면, Execute Call 을 줄인다.


DB저장형 사용자정의 함수/프로시저
- 가상머신 상에서 실행되는 Interpreting 언어다.
- 함수가 실행될때마다 Recursive Call 이 발행한다.
- Join 대신, 함수를 사용하여 코드명 등을 조회하면, 성능이 나빠진다.

DB저장형 함수 성능개선
- Join 문으로 변경
- Deterministic 함수로 전환
- Result 캐시 사용

DB저장형 함수 Recursive Call 횟수 개선
- 1. 함수 호출을 스칼라 서브쿼리로 변환한다 (스칼라 서브쿼리 캐싱) : ( select code_nm(1, 2) from dual ) cd_name
- 2. Deterministic 함수 캐싱
- 3. Result 캐시
- 4. Native 컴파일




파티셔닝 (Partitioning)
- 테이블데이터 또는 인덱스 데이터를 나누어서 저장하는 기능
- 다른 파티션에 대량 변경/백업/복구 중에도, 조회/변경/삭제 가능

Range 파티션
- 여러 컬럼 지정 가능
- 숫자,문자,날짜 컬럼 사용 가능
- 범위에 없는 값이 들어왔을 때 오류를 방지하기 위해 MAXVALUE 파티션을 사용해야 한다.
- 신규 파티션을 추가해야 하는 관리 이슈가 있다.

partion by range(주문일시) (
     partition p20201 values less than( to_date('20200701', 'YYYYMMDD') ),
     partition p20202 values less than( to_date('20210101', 'YYYYMMDD') ),
     partition p20211 values less than( to_date('20210701', 'YYYYMMDD') ),
     partition p20212 values less than( to_date('20220101', 'YYYYMMDD') ),
     partition pMAX values less than( MAXVALUE )
)



List 파티션
- 단일 컬럼만 지정 가능
- 불연속적인 값의 목록으로 분할할때 사용
- 범위에 없는 값이 들어왔을 때 오류를 방지하기 위해 DEFAULT 파티션을 사용해야 한다.

 

partition by list(gubun) (
     partition p_list1 values(10),
     partition p_list2 values(20),
     partition p_list3 values(30),
     partition p_list3 values(40),
     partition p_list4 values(DEFAULT)
);



Hash 파티션
- 여러 컬럼 지정 가능
- DML 경합을 분산하는데 효과적이다.
- 값의 범위가 고르지 않으면, 특정 파티션에 데이터가 몰리게 된다.
- Hash 알고리즘으로 분산이 되기 때문에, 입력공간을 못 찾는 오류는 발생하지 않는다.

 

PARTITION BY HASH ( EmpNo ) (
     PARTITION P_H1,
     PARTITION P_H2,
     PARTITION P_H3,
     PARTITION P_H4
);
PARTITION BY HASH ( EmpNo ) 
PARTITIONS 4;

 



Pruning
- SQL 파드파싱이나 실행시점에 조건절을 분석해서, 읽지말아야 할 파티션을 제외하는 기능
- 문자형 날짜값에 Like 조건을 사용하면 불필요한 파티션을 읽게 된다. Between 조건으로 변경하는것이 좋다.
- 문자형 날짜값에 숫자로 검색하면, 형변환이 발생해 전체 파티션을 조회하게 된다.
- 정적 Pruning / 동적 Pruning 성능 차이는 없다.

PARTITION RANGE ALL
- 파티션 컬럼을 가공하면, 파티션 Pruning 이 불가능 해, 전체 파티션을 조회 하게 된다.
- 파티션 컬럼 조건에 다른 타입의 값을 넣을경우, 강제 형변환이 발생하여, 전체 파티션을 조회 할수 있으니 주의해야 한다.

정적(Static) 파티션 Pruning
- 조건절에 파티션 키 컬럼이 상수로 들어있는 경우
- 쿼리 최적화 시점에 액세스할 파티션 번호를 알 수 있다.
- 실행계획 Pstart, Pstop 에 파티션 번호가 표시된다.

동적(Dynamic) 파티션 Pruning
- 조건절에 파티션 키 컬럼이 바인드 변수로 들어있는 경우, 다른 테이블과의 Join 조건일 경우.
- 실행 시점에 액세스할 파티션을 알 수 있다.
- 실행계획 Pstart, Pstop 에 KEY 라고 표시된다.


Partition Index
- Local Partition Index 와 Global Partition Index 로 구성된다.

Local Partition Index
- 파티션 테이블 내의 데이터로만 인덱스가 구성된다.
- Prefixed Index 와 Non-Prefixed Index 로 구성된다.
- 테이블 파티션을 재구성하면, Local Index 는 자동으로 Rebuild 되어 관리의 편의성이 있다.

create index dept_local_ix1 on dept(deptno) LOCAL;


Local Prefixed Index
- 파티션 컬럼이 인덱스 인 경우 (인덱스가 여러 컬럼일때 선두 컬럼)

Local Non-Prefixed Index
- 인덱스된 컬럼이 파티션 컬럼이 아닌 경우 (인덱스가 여러 컬럼일때, 선두 컬럼이 파티션 컬럼이 아닐 때)


Global Partition Index
- 파티션 테이블과는 별도로 인덱스 파티션 규칙으로 파티션을 구성한다.
- Non-Partition Table 에도 생성 가능하다.
- Prefixed Index 만 존재한다. (Non-Prefixed Index 불가)
- 테이블 파티션을 재구성하면, Index Unusable 상태가 되어, Rebuild 가 필요하다.

create index sales_global_ix1 on sales (cust_nm) GLOBAL
partition by hash (cust_nm)
partitions 4;



Index Unusable(IU) 상태
- Partition Index / Non-Partition Index 가 사용불가 상태가 되는 경우.
- IU 파티션을 사용하려고 하면 오류가 발생한다. IU 파티션을 Rebuild 해야 한다.
- 1. Direct Path Load 시 index 가 해당 테이블 data 보다 이전것인 경우
- 2. Alter Table Move Partiton : ROWID 를 변화시키는 작업
- 3. Alter Table Truncate Partition / Drop Partition 과 같은 테이블의 행을 지우는 작업
- 4. Alter Table Split Partition
- 5. Alter Index Split Partition



배치(Batch) 프로그램
- 일련의 대량 작업을 묶어서 일괄 처리

배치프로그램 튜닝
- 전체 처리속도 최적화에 목표
- Array Processing 을 활용
- 임시 테이블을 활용
- 병렬처리를 적절히 활용. 단, 필요 이상의 병렬도(DOP) 를 지정하지 않아야 한다.

병렬처리
- 병렬 처리란 SQL문이 수행해야 할 작업 범위를 여러 개의 작은 단위로 나누어 여러 프로세스가 동시에 처리

QC(Query Coordinator)
- 병렬 SQL문을 발행한 세션
- 병렬 서버 집합을 할당
- 각 병렬 서버에게 작업을 할당
- 병렬로 처리하도록 사용자가 지시하지 않은 테이블은 QC가 처리

Intra-Operation Parallelism
- 병렬 처리에서 서로 배타적인 범위를 독립적으로 동시에 처리하는 것

Inter-Operation Parallelism
- 프로세스 간의 통신을 뜻한다.
- 다른 서버집합에 분배하거나 정렬된 결과를 QC에서 전송하는 작업을 병렬로 동시에 진행하는 것
- 병렬도(DOP) 의 2배의 프로세스가 필요하고, 병렬도(DOP) 의 제곱만큼 파이프 라인(테이블 큐)이 필요

S->P (Parallel from Serial)
- QC가 읽은 데이터를 테이블 큐를 통해 모든 병렬 서버 프로세스에게 전송
- BROADCAST 라고 표시하며, Inter-Operation Parallelism 에는 속하지 않는다.
- 데이터 크기가 작을수록 유리

P->S (Parallel to Serial)
- 각 병렬 서버 프로세스가 처리한 데이터를 QC에게 전송하는 것
- Inter-Operation Parallelism 에 속한다.
- order by가 있을 때는 QC(ORDER)
- order by가 없을 때는 QC(RANDOM)

P->P (Parallel to Parallel) 오퍼레이션
- 한 서버 집합이 반대편 서버 집합에 데이터를 전송하는 것
- 데이터를 재분배하는 오퍼레이션으로 데이터를 정렬하거나 그룹핑, 조인을 위한 동적으로 파티셔닝을 할 때 발생
- Inter-Operation Parallelism 에 속한다.

PCWP (Parallel Combined with Parent)
- 한 서버 집합이 현재 스텝과 그 부모 스텝을 모두 처리
- Intra-Operation Parallelism 에 속한다.

PCWC (Parallel Combined with Child)
- 한 서버 집합이 현재 스텝과 그 자식 스텝을 모두 처리
- Intra-Operation Parallelism에 속한다.


parallel_index
- 블록 기반 Granule : Full Table Scan 일때는 병렬도(DOP) 를 파티션 사이즈보다 크게 지정할 수 있다.
- 파티션 기반 Granule : Index Range Scan 일때는 병렬도(DOP) 를 파티션 사이즈만큼 지정해야 효율적이다.


Hint

PARALLEL
- 병렬 처리 하도록 한다.
- PARALLEL(table, dop)
- 반드시 FULL 힌트를 같이 사용한다.

PARALLEL_INDEX
- 인덱스 범위 스캔을 병렬로 처리
- PARALLEL_INDEX(table, index, /* dop */)
- 반드시 INDEX 또는 INDEX_FFS 힌트를 같이 사용한다.


group by / order by / hash join 을 사용하면, 병렬프로세스가 dop 의 2배 만큼 필요하다.



PQ_DISTRIBUTE
- 병렬 Join 시에 데이터 분배 방식을 직접 결정
- PQ_DISTRIBUTE(inner_table, outer_distribution, inner_distribution)

pq_distribute(inner_table, hash, hash)
- 병렬조인을 위해 해시함수를 사용하도록 지정
- 실행계획에 PX SEND HASH 가 나타난다.

pq_distribute(inner_table, broadcast, none)
- outer table 의 모든 데이터를 병렬서버에 전송
- outer table 의 데이터 크기가 작을때 사용
- 실행계획에 PX SEND BROADCAST 가 나타난다.

pq_distribute(inner_table, none, broadcast)
- inner table 의 모든 데이터를 병렬서버에 전송
- inner table 의 데이터 크기가 작을때 사용
- 실행계획에 PX SEND BROADCAST 가 나타난다.

pq_distribute(inner_table, partition, none)
- Partial Partition-wise Join
- inner table 이 조인 키 컬럼에 대해 파티셔닝 돼 있을 때 사용
- 실행계획에 PX SEND PARTITION 이 나타난다.

pq_distribute(inner_table, none, partition)
- Partial Partition-wise Join
- outer table 이 조인 키 컬럼에 대해 파티셔닝 돼 있을 때 사용
- 실행계획에 PX SEND PARTITION 이 나타난다.

pq_distribute(inner_table, none, none)
- Full Partition-wise Join
- join 되는 두 테이블 모두 Join Key 컬럼으로 파티션되어 있는 경우에 사용
- 실행계획에 PX PARTITION RANGE ALL 이 나타난다.
- 하나의 서버 집합만 필요.



ROWNUM
- 병렬처리 쿼리에 ROWNUM 을 사용하면, 병렬프로세스에 병목이 발생한다.

rownum 을 row_number() 바꿔서 사용해야 한다.

select rownum() as 일련번호, 주문일자, 순번
from (
select /*+ parallel(t_order 4) 주문일자, 순번
from t_order
order by 주문일자, 순번
)
=>
select /*+ parallel(t_order 4) row_number() over(order by 주문일자, 주문순번) as 일련번호, 주문일자, 순번
from t_order




데이터 복제 기법


데이터 복제를 이용한 소계 구하기
- 카티션 곱(Cartesian Product) 을 이용

select rownum as no from dual connect by level <= 2;
1
2



select deptno 부서번호
, decode(no, 1, to_char(empno), 2, '부서계') 사원번호
, sum(sal) 급여합
, round(avg(sal)) 급여평균
from emp e, (select rownum as no from dual connect by level <= 2)
group by deptno, no, decode(no, 1, to_char(empno), 2, '부서계')
order by 1, 2;





unpivot (열데이터를 행데이터로 변환)

SELECT *
FROM 테이블 UNPIVOT (
     컬럼별칭(값) FOR 컬럼별칭(열) IN (피벗열명 AS '별칭', ...
)



select 고객번호, 고객명, 연락처구분, 연락처번호
from 고객 unpivot (
   연락처번호 for 연락처구분 in (
       집전화번호 as '집전화번호'
       ,  사무실전화번호 as '사무실전화번호'
       , 휴대폰번호 as '휴대폰번호'
   )
)
where 고객구분코드 = 'VIP'





pivot (행데이터를 열데이터로 변환)

SELECT *
FROM 테이블 PIVOT (
집계함수 FOR 대상필드 IN (필드값 목록)
)



select C.고객번호, C.고객명, D.집, D.사무실, D.휴대폰
from 고객 C
    , 고객연락처 pivot (
        min(연락처번호) for 연락처구분코드 in ( 'HOM' as '집', 'OFC' as '사무실', 'MBL' as '휴대폰' )
    ) D
where C.고객번호 = D.고객번호
and C.고객구분코드 = 'VIP'





LISTAGG

행을 문자열로 합치는 기능 (pivot)

LISTAGG([합칠 컬럼명], [구분자]) WITHIN GROUP(ORDER BY [정렬 컬럼명])
(하단에 group by 사용시)

LISTAGG([합칠 컬럼명], [구분자]) WITHIN GROUP(ORDER BY [정렬 컬럼명]) OVER(PARTITION BY [그루핑 컬럼명])
(하단에 group by 미사용시)





분석함수 : 누적값

sum(매출금액) over(partition by 지점코드 order by 판매월
    range between unbounded preceding and current row)
) 누적매출

부등호조인 : 누적값

select A.지점코드, A.판매월, min(A.매출금액) 매출금액
   , sum(B.매출금액) 누적매출
from 월별지점매출 A, 월별지점매출 B
where A.지점코드 = B.지점코드
   and A.판매월 >= B.지점코드
group by A.지점코드, A.판매월
order by A.지점코드, A.판매월



with 구문 (CTE)

materialize 방식
- 임시 테이블을 사용하여 반복 재사용, 실행계획에 TEMP TABLE TRANSFORMATION 이 나타남.
- 사이즈가 큰 데이터를 읽어 GROUP BY, 조인 등으로 집합크기를 줄일 수 있을때 사용.
- /*+ materialize */

inline 방식
- 참조 횟수만큼 런타임에서 반복 실행됨
- /*+ inline */

 

 

 

Posted by 헝개
데이터베이스2025. 3. 5. 09:33

SQLP : 제3과목은 SQL 고급활용 및 튜닝 이다.
 
 
3과목. SQL 고급활용 및 튜닝
1. SQL 수행 구조

2. SQL 분석 도구

3. 인덱스 튜닝

4. 조인 튜닝

5. SQL 옵티마이저

6. 고급 SQL 튜닝

7. Lock과 트랜잭션 동시성 제어

 

 

 

 

 

비용기반 옵티마이저 (CBO)
- 통계정보 : 데이터양, 컬럼값의 수, 컬럼값 분포, 인덱스 높이, 클러스터링 팩터, 최대값, 최소값, NULL개수, 히스토그램 등
- 시스템 통계정보 : CPU속도, Single Block I/O 속도, Multi Block I/O 속도, Multi Block 개수 등
- 통계정보를 사용해서 실행계획을 세운다.
- 실행계획 선택에 대한 내부 규칙이 있다.
- 효율적이다.
- 버퍼 캐싱 효과는 고려하지 않는다. (디스크 I/O 를 기준으로 한다.)

규칙기반 옵티마이저 (RBO)
- 통계정보를 전혀 사용하지 않고, 단순히 규칙에만 의존한다.
- 대량 데이터 처리에 적합하지 않다.
- 인덱스 구조, 연산자, 조건절의 형태로 우선순위 규칙에 따라 최적화 한다.
- 인덱스가 있으면 무조건 인덱스를 사용한다. : Full Table Scan 이 우선순위가 가장 낮다.
- 부등호보다 between 이 우선순위가 높다.

옵티마이저 서브엔진
- Query Transformer : 쿼리 변환 과정
- Estimator : 예상 비용 산정
- Plan Generator : 실행계획 후보군 생성

스스로 학습하는 옵티마이저 (Self Learning Optimizer)
- Adaptive Cursor Sharing : 바인드변수에 따라 실행계획을 선택적으로 사용
- Statistics Feedback : 실행계획의 로우수와 실제 로우수가 다를때, 다음번에 이전 로우수에 따른 실행계획이 수립되도록 함
- Adaptive Plans : 런타임에 실행계획을 변경


최적화

전체 처리속도 최적화 (ALL_ROWS)
- 전체를 읽기 때문에 시스템리소스(I/O, CPU, 메모리) 를 적게 사용하는 실행계획을 선택

최초 응답속도 최적화 (FIRST_ROWS, FIRST_ROWS_N)
- 응답속도가 빠른 실행계획을 선택
- 2-Tier 클라이언트/서버 구조 : 커서를 오픈해서 데이터를 Fetch 하다가 중간에 멈출 수 있는 기능
- 인덱스를 많이 선택하고, 해시조인보다 NL 조인을 더 많이 선택한다.

SQL 최적화 과정
- 테이블, 컬럼, 인덱스 정보, 각각 통계 정보를 사용한다.
- 시스템 통계정보(CPU 속도 등) 을 사용한다.
- 옵티마이저 관련 파라미터 사용
- 통계 정보를 사용한다. 통계 정보를 저장하지는 않는다.

SQL 파싱과 최적화 순서
1. Parser : SQL 개별 구성요소를 분석해서 파싱트리를 만든다.
2. Query Transformer : SQL 을 일반적이고 표준적인 형태로 변환
3. Plan Generator & Estimator : 실행 후보군을 도출하고, 비용을 계산
4. Row-Source Generator : SQL 엔진이 실행할 수 있는 프로시저 형태로 포매팅


SQL 파싱
- 소프트파싱 : SQL 이 라이브러리 캐시에 존재하면 이를 사용하여 실행
- 하드 파싱 : SQL 이 라이브러리 캐시에 존재하지 않으면 최적화 및 로우 소스 생성 단계까지 모두 거쳐서 실행

선택도(Selectivity)
- 조건절에 의해 선택될 것으로 예상되는 레코드 비중 : 1 / NDV

카디널리티(Cardinality)
- 조건절에 의해 선택될 것으로 예상되는 레코드 수 : 전체레코드수 * 선택도

I/O 비용 모델의 비용(Cost)
- 예상되는 디스크 I/O Call 횟수

CPU 비용 모델의 비용(Cost)
- 예상소요시간을 Single Block I/O 횟수로 환산한 값


서브쿼리
- 하나의 SQL 문장에서 괄호로 묶은 별도의 쿼리 블록

인라인 뷰
- FROM 절에 나오는 서브쿼리로 레코드를  선택하는 쿼리다.

중첩된 서브쿼리
- WHERE 절이나 HAVING 절과 같은 조건절에서 사용되는 서브쿼리로, 메인 쿼리의 데이터를 필터링하거나 비교할 때 사용


스칼라 서브쿼리
- 주로 SELECT 절이나 다른 쿼리의 일부로서 단일 값을 반환하는 서브쿼리



no_unnest
- 중첩된 서브쿼리를 각각 그대로 FILTER 방식으로 처리한다.
- 서브쿼리의 결과가 작을 때 유리하다. : 캐싱
- 조건절의 스칼라 서브쿼리 : 실행계획에 FILTER 로 나타나게 된다.

unnest
- 중첩된 서브쿼리를 풀어낸다.
- 조건절의 스칼라 서브쿼리 : 실행계획에 NL 조인으로 나타나게 된다.
- unnest 를 사용하게 되면, rownum 조건을 제거해야 한다.

use_concat
- OR 조건을 union all 로 변환해준다.

- where job = 'cleark' or deptno = 20
=>
... where job = 'cleark'
union all
... where deptno = 20



no_expand
- OR 조건을 union all 로 변환하지 않고 그대로 수행하도록 한다.


merge
- 뷰머징 : 인라인뷰를 해체하여 상위 쿼리로 올린다.

no_merge
- 인라인뷰를 해체하지 않고 그대로 수행

push_subq
- 일반적으로, 조건절의 FILTER 조건인 서브쿼리는 제일 마지막에 실행된다.
- push_subq 를 사용하면, FILTER 조건인 서브쿼리를 먼저 실행되도록 한다.
- /*+ no_unnest push_subq */


push_pred
- 메인테이블과 인라인뷰의 JOIN 시에, 조인조건을 인라인뷰 안으로 유도한다. : 인라인뷰 성능향상
- /* no_merge push_pred */



바인드 변수
- 파라미터 드리븐 방식으로 SQL을 작성하는 방법
- SQL 의 재사용 가능
- 바인드 변수 사용 시 옵티마이저는 균등 분포 가정하고 비용을 계산한다.


애플리케이션 커서 캐싱
- Parse Call을 발생시키지 않고 SQL을 반복 수행하는 기능
- Parse Call이 최초 한번만 발생하고 이후로는 발생하지 않는다.

 

 

Posted by 헝개
데이터베이스2025. 3. 4. 13:52

SQLP : 제3과목은 SQL 고급활용 및 튜닝 이다.
 
 
3과목. SQL 고급활용 및 튜닝
1. SQL 수행 구조

2. SQL 분석 도구

3. 인덱스 튜닝

4. 조인 튜닝

5. SQL 옵티마이저

6. 고급 SQL 튜닝

7. Lock과 트랜잭션 동시성 제어

 

 

 

 

 

NL(Nested-Loop) 조인
- 중첩된 Loop 로 조인을 하는 구조이다.

for( ... )
{
     for( ...)
     {
        ...
     }
}


- 랜덤 액세스 위주의 조인 방식 : 대량 데이터에서 불리
- 소량데이터 조인시 유리
- DB버퍼캐시에서 읽으므로, 블록에 래치 획득 및 체인 스캔 과정을 거친다. : 대량 데이터에서 불리
- 인덱스를 이용한 조인으로, 인덱스 구성 전략에 의해 성능이 크게 달라진다.
- 후행 테이블의 인덱스 효율성이 중요하다.
- 소량 데이터에 유리, OLTP 시스템에 적합
- 한 레코드씩 순차적으로 진행 : "부분범위 처리"가 가능
- 조인 대상 레코드가 많아도, ArraySize 에 해당하는 최초 n 건을 빠르게 출력 가능


소트머지조인
- 실시간으로 인덱스를 생성한다.
- 첫번째 집합은 조인컬럼에 인덱스가 있으면 인덱스를 사용하고, 없으면 PGA 영역에 저장한다.
- 두번째 집합은 정렬해서 PGA 영역에 저장한다.
- 조인조건이 = 이 아닐때, 조인조건이 없을때도 사용가능하다.
- 버퍼영역이 아닌 PGA 영역에서 조회하므로, 대량 데이터에서 NL 조인보다 빠르다. : 래치 획득 과정이 없어서

Hash 조인
- 작은 집합을 읽어서 PGA 에 해시맵으로 저장한다.
- 해시맵을 사용하여 탐색하기 때문에 조인컬럼에 인덱스가 없어도 상관이 없다. : 인덱스가 성능에 영향을 주지 않음
- 소트머지조인보다 Temp테이블스페이스 사용량이 적다.
- 해시키로 조회하므로 조인 조건이 = 일때만 사용가능하다.
- 양쪽 모두 대량 데이터에서 NL 조인보다 빠르다.
- 수행빈도가 낮고, 쿼리 수행시간이 오래 걸리는 대량 데이터 조인시 유리하다.
- DW/OLAP / Batch 프로그램에 사용.

 


join 힌트

Oracle

ordered

/*+ ordered */
- FROM 절에 나열된 순서대로 조인

leading

/*+ leading(a b c) */
- a b c 순서대로 조인


use_nl( a )
- NL 조인 힌트


use_hash( a )
- 해시 조인 힌트


use_merge( a )
- 소트 머지 조인 힌트


nl_sj
- NL 세미조인 : 서브쿼리에서 사용


swap_join_inputs( a )
- a 를 build input 으로 한다. (조인의 첫번째 집합) : 해시조인에서 사용

no_swap_join_inputs ( a )
- a 를 probe input 으로 지정한다. (조인의 두번째 집합) : 해시조인에서 사용


NO_MERGE
- 메인쿼리와 인라인뷰를 병합하지 말고, 인라인뷰부터 실행한다. : 인라인뷰에 함수 등이 쓰일때, 한번만 실행하도록 유도

PUSH_PRED
- 메인테이블과 인라인뷰의 JOIN 시에, 조인조건을 인라인뷰 안으로 유도한다. : 인라인뷰 성능향상


NO_UNNEST
- 서브쿼리를 필터 방식으로 동작하도록 처리 : 부분범위 처리가 가능해서, 해시 조인으로 처리되던거를 NL 조인으로 변경이 된다.
 
 FULL 
 - 테이블 풀 스캔

 PUSH_SUBQ
 - 서브쿼리가 먼저 실행되도록 제어

QB_NAME( name )
- 현재 쿼리블록의 이름을 지정


index(table index)
- table 의 index 를 사용하도록 한다.

index_desc
- 인덱스를 역순으로 사용

index_ffs
- 인덱스 Fast Full Scan 사용


use_concat
- Concatenation  오퍼레이션 : OR 조건을 풀어서, Union All 조건의 쿼리로 변환한다. : or_expand

no_expand
- use_concat 이 작동하지 않도록 한다. : no_or_expand 



MS-SQL

force order

option( merge join)
- FROM 절에 나열된 순서대로 조인

merge join
option( merge join )
- 소트 머지 조인 힌트


loop join
- NL 조인 힌트

inner loop join

from A
inner loop join B on A.col1 = B.col2

inner hash join
 



스칼라 서브 쿼리

- 스칼라 서브 쿼리는 단일 값만을 반환해야 한다. (스칼라)
- 오라클에서는 서브쿼리 입출력값을 내부캐시에 저장해 두기 때문에 성능향상에 좋다.
- 서브쿼리의 효율은 코드명이나 상품명을 변환시 사용 : 카디널러티가 적은 테이블에 사용하면 좋다.

스칼라 서브쿼리 Execution Plan
- 서브쿼리가 위에 나오고, 메인쿼리가 아래에 나온다.


부분범위처리
- 조건을 만족하는 전체범위를 처리하는 것이 아니라 일단 운반단위(Array Size) 까지만 처리 하여 추출하는 처리방식
- TOP n / ROWNUM 사용 등
- 인덱스 사용이 가능하도록 조건절을 구사하고, 조인은 NL위주로 처리하고, Order By절이 있어도 소트연산을 생략할 수 있도록 인덱스를 구성 : index 의 2번째 컬럼부터 order by 컬럼을 추가
- 페이징 처리 (게시판 등)

 

 

Posted by 헝개
데이터베이스2025. 3. 3. 11:44

SQLP : 제3과목은 SQL 고급활용 및 튜닝 이다.

 

 

3과목. SQL 고급활용 및 튜닝

1. SQL 수행 구조
2. SQL 분석 도구
3. 인덱스 튜닝
4. 조인 튜닝
5. SQL 옵티마이저
6. 고급 SQL 튜닝
7. Lock과 트랜잭션 동시성 제어

 

 

 

 

B*Tree 인덱스 (Balanced Tree) 구조
- 브랜치 블록의 각 로우는 하위블록에 대한 주소값을 갖는다.
- 리프 블록의 키 값은 테이블 로우의 키 값과 같다.
- 리프 블록 끼리는 이중연결리스트(double linked list) 구조이다.

인덱스 ROWID
- (오브젝트번호 + ) 데이터파일번호 + 블록번호 + 블록내로우번호
- 데이터파일 상의 블록 위치를 가리킨다. 논리적인 주소 (물리적인 주소는 아니다.)
- 버퍼 캐시에서 찾을때는 해시 알고리즘을 이용한다.


인덱스 클러스터링 팩터
- 인덱스에 저장된 순서와 테이블에 저장된 데이터의 저장 순서가 얼마나 일치하는지를 나타내는 값 
- 최대값은 인덱스에 저장된 항목의 개수 
- 최소값은 테이블 블록의 개수

테이블 블록 수에 가까울수록 좋고, 테이블 레코드 수에 가까울수록 나쁘다.

인덱스 클러스터링 팩터가 좋으면, 테이블 액세스 단계에서 읽어들이는 블록(데이터) 가 줄어든다.
인덱스 리빌드 해도 클러스터링 팩터가 좋아지지 않는다. 테이블을 재생성 해야 한다.




인덱스에는 구성 컬럼값이 모두 NULL 인 값은 저장하지 않는다.


IOT (index organizaion table)
- Table Random 엑세스가 발생하지 않도록 테이블을 아예 인덱스 구조로 생성
- PK 순으로 정렬상태를 유지한다.
- IOT 테이블의 일반 인덱스에는 PK 컬럼도 함께 저장된다.

- MS-SQL 의 Clustered Index 와 유사한 개념


인덱스 스캔 방식

Index Range Scan
- 인덱스 루트에서 리프 블록까지 수직적 탐색을 하고, 필요한 범위만큼 수평적 탐색하는 스캔 방식
- 인덱스 선두컬럼이 조건절에 있어야 한다.

Index Unique Scan
- 수직적 탐색으로만 데이터를 찾는 스캔 방식
- 인덱스 컬럼이 모두 조건절에 있는 경우( = 조건)

Index Skip Scan
- 루트 또는 브랜치 블록에서 읽은 컬럼 값 정보를 이용해서 조건절에 부합하는 레코드를 포함할 가능성이 있는 리프 블록만 액세스
- 인덱스 선두 컬럼이 조건절에 없어도 인덱스를 활용하는 새로운 스캔 방식
- 조건절에 빠진 선행 인덱스 컬럼의 Distinct Value 개수가 적고, 후행 컬럼의 Distinct Value 개수가 많을 때 유용
(선행컬럼이 datetime 일때는 불리, 날짜값(년월일)만 있을때 유리)

- 힌트 : index_ss
- 선두 컬럼이 부등호, between, like 같은 범위 조건일때도 사용 가능
- 선두 컬럼이 in ( list ) 조건일때는 사용 불가

Index Full Scan
- 수직적 탐색 없이 인덱스 리프 블록 처음부터 끝까지 수평적으로 탐색하는 방식
- 인덱스 힌트 사용하고, 해당 인덱스의 선두컬럼이 조건절에 없는 경우 사용된다.
- 인덱스 필터 조건을 만족하는 데이터가 적어야 효과적이다.

Index Fast Full Scan
- 논리적인 인덱스 트리 구조를 무시하고, 인덱스 세그먼트 전체를 Multiblock I/O 방식으로 스캔
- 물리적으로 디스크에 저장된 순서대로 인덱스 리프 블록을 읽음.
- 결과 레코드의 순서는 실제 데이터 순서와 다르게 나옴
- 힌트 : index_ffs

Index Range Scan Descending
- Index Range Scan과 기본적으로 동일한 스캔, 인덱스를 뒤에서부터 앞쪽으로 스캔
- 힌트 : index_desc


인덱스 컬럼의 가공
- 인덱스 컬럼이 가공되면, index range scan 이 불가해 진다.
- 부정형 비교는 index range scan 이 불가하다.
- is not null 조건은 index range scan 이 불가하다.

where substr(업체명, 1, 2) = '대한'
=>
where 업체명 like '대한%'
where 월급여 * 12 = 36000000
=>
where 월급여 = 36000000 / 12




묵시적 형변환

문자와 숫자를 비교하는 경우, 자동으로 숫자로 형변환이 일어난다.

where 숫자형DeptNo = '20'
=> 숫자형DeptNo = to_number('20') 로 형변환이 일어나고, 인덱스 사용 가능
where 문자형DeptNo = 20
=> to_number(문자형DeptNo) = 20 로 형변환이 일어나고, 인덱스 사용 불가




테이블 Random 액세스

인덱스 Leaf 노드에는 ROWID 가 있어, 인덱스에 없는 컬럼을 참조시,
테이블 ROWID 로 Random 액세스를 하게 된다.

실행계획에는 아래와 같이 표시된다.

TABLE ACCESS (BY INDEX ROWID) OF '테이블명' (TABLE)



테이블 랜덤 액세스가 많아지면, 전체 테이블 스캔(Full Table Scan) 보다 느려질 수 있다.
(인덱스 손익 분기점)



배치 I/O
- 테이블 랜덤 액세스시, 버퍼에서 블록을 찾지 못하면, 디스크에서 읽게 된다.
이때, 매번 디스크에서 읽지 않고, 모았다가, 일괄로 찾아오는 방법으로 I/O 효율을 높이는 방법이다.

배치 I/O 가 수행되면, 랜덤 액세스 방식이 아래처럼 변경된다.

TABLE ACCESS BY INDEX ROWID
=>
TABLE ACCESS BY INDEX ROWID BATCHED



배치 I/O 가 수행되면, 버퍼에 없는 블록끼리 모아서 처리 되므로, 결과집합의 정렬 순서가 바뀌게 되고,

실행결과에 SORT ORDER BY 가 나타나면, 부분범위 처리가 불가능해 진다.




인덱스 튜닝

col1 이 인덱스 선두 컬럼일 때, in (list) 방식이 유리하다.

where col1 <> 3
=> where col1 in (0, 1, 2, 4, 5, 6)
where col1 between 'A' AND C
=> where col1 in ('A', 'B', 'C')



col2 가 인덱스 후행 컬럼일 때는 in(list) 보다 filter 방식이 유리하다.

where col1 = :val1 and col2 in(0, 1, 2, 4, 5, 6)
=> where col1 = :val1 and col2 <> 3




선두컬럼 "or" 조건은 union all 로 처리하는게 유리하다.

select /*+ use_concat */
from
where col1 like 'A%' or col1 like 'B%'



후행컬럼 or 조건은 union all 로 처리하는게 불리하다.

select /*+ no_expand */
from
where col1 = 'ABC' and ( col2 like 'A%' or col2 like 'B%' )






LIKE 검색

like :val || '%'

시작값으로 조회할때만 index range scan 이 가능하다.
null 허용 컬럼에서 null 값을 찾을때는 like '%' 로 변환되어, null 인 값을 못 찾게 된다.
숫자형 컬럼일때 또는 가변 컬럼일때..   123 인 값을 찾고자 해도, 앞에 123으로 시작하는 모든 데이터를 찾게 된다. (결과의 오류)
index 선두 컬럼에 대해 like 검색시 null 값으로 조회하면, 모든 index 를 찾아야 하므로, 비효율이 발생한다.



소트연산 생략

WHERE col1 = 'ABC'
ORDER BY col2, col3



실행계획에 ORDER BY 가 나타나야 하지만, 인덱스에 있으면, ORDER BY 가 생략된다.
인덱스에 2번째부터 순서대로 order by 컬럼이 나와야 한다.

인덱스 구성 : col1 + col2 + col3

소트 연산이 생략되면, 부분범위 처리가 가능해진다.





MS-SQL 인덱스 힌트

PK 인덱스 사용, PK(Clustered Index) 가 없는 테이블은 테이블 풀 스캔
select * from tablename with(index(0))

PK 인덱스 사용, PK(Clustered Index) 가 없는 테이블은 오류
select * from tablename with(index(1))

인덱스명 사용
select * from tablename with(index=table_ix1)
select * from tablename with(index(table_ix1))

테이블 풀 스캔
select *
from
where ...
option( forcescan )

select *
from tablename
where ...
option( table hint( tablename, index(table_ix1) ) )

select *
from tablename with ( forceseek(table_ix1 (col1) ) )

Posted by 헝개
데이터베이스2025. 3. 2. 14:26

SQLP : 제3과목은 SQL 고급활용 및 튜닝 이다.

 

 

3과목. SQL 고급활용 및 튜닝

1. SQL 수행 구조
2. SQL 분석 도구
3. 인덱스 튜닝
4. 조인 튜닝
5. SQL 옵티마이저
6. 고급 SQL 튜닝
7. Lock과 트랜잭션 동시성 제어

 

 

 

 

 

예상 실행계획 표시

 

DBMS_XPLAN.DISPLAY
- 단일 SQL문에 대해 "예측" 실행계획

explain plan for
SELECT * FROM Product WHERE ProductID = 1;

select * from table(dbms_xplan.display(null, null, 'typical'));

 

 

TYPICAL 포맷

--------------------------------------------------------------------------------------
| Id  | Operation            | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |         |       1 |      20 |       1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP    |       1 |      20 |       1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN        | PK_EMP |       1 |         |       1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("EMPNO"=7900)

14 rows selected.

 

 

ALL 포맷

--------------------------------------------------------------------------------------
| Id  | Operation            | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |         |       1 |      20 |       1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP    |       1 |      20 |       1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN        | PK_EMP |       1 |         |       1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$1 / EMP@SEL$1
   2 - SEL$1 / EMP@SEL$1
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("EMPNO"=7900)
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - "EMPNO"[NUMBER,22], "ENAME"[VARCHAR2,10]
   2 - "EMP".ROWID[ROWID,10], "EMPNO"[NUMBER,22]
 
26 rows selected.

 

 

 

DBMS_XPLAN.DISPLAY_CURSOR
- 단일 SQL문에 대해 "실제" 수행된 실행계획

 

ALTER SESSION SET STATISTICS_LEVEL = ALL;
SELECT * FROM Product WHERE ProductID = 1;
SELECT * FROM TABLE(dbms_xplan.display_cursor(null, null, 'typical');


SELECT /*+ gather_plan_statistics */ * FROM Product WHERE ProductID = 1;
SELECT * FROM TABLE(dbms_xplan.display_cursor(null, null, 'allstats last');

 

 

TYPICAL 포맷

--------------------------------------------------------------------------------------
| Id  | Operation            | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |         |         |         |       1 (100)|         |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP    |       1 |      20 |       1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN        | PK_EMP |       1 |         |       1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("EMPNO"=7900)
 
 
20 rows selected.

 

 

ALL 포맷

--------------------------------------------------------------------------------------
| Id  | Operation            | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |         |         |         |       1 (100)|         |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP    |       1 |      20 |       1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN        | PK_EMP |       1 |         |       1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$1 / EMP@SEL$1
   2 - SEL$1 / EMP@SEL$1
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("EMPNO"=7900)
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - "EMPNO"[NUMBER,22], "ENAME"[VARCHAR2,10]
   2 - "EMP".ROWID[ROWID,10], "EMPNO"[NUMBER,22]
 
 
32 rows selected. 

 

 

 

Id
실행계획에서의 구분자
Operation
각 단계에서의 어떤 작업이 일어났는지 표시
Name
테이블명이나 index 명을 표시함
Rows
해당 쿼리 계획 단계에서 나올 것으로 예상되는 행의 수
Byte
실행 계획의 각 단계가 반환할 것으로 예상 되는 데이터의 크기를 바이트로 나타낸 수. 이 수는 행의 수와 행의 예상 길이에 따라 결정됨
Cost
CBO가 쿼리 계획의 각 단계에 할당한 비용. CBO는 동일한 쿼리에 대해 다양한 실행 경로/계획을 생성하며 모든 쿼리에 대해 비용을 할당함
위의 실행 계획에선 전체 비용이 13인것을 알수 있음
Time
각 단계별 수행 시간
Query Block Name
SQL 문장을 Query Block이라는 단위로 나눔. inline view 와 subquery 등
Outline Data
실행 계획을 수립 하는데 필요한 내부적으로 적용된 Hint들의 목록
Predicate
조건절
Column Projection
실행 계획의 특정 단계에서 어떤 Column을 추출하는가를 의미

 

 

 

ADVANCED ALLSTATS LAST 포맷

 

 

Starts
말 그대로 해당 오퍼레이션이 "시작"된 횟수를 의미함
이 개념은 Nested Loops Join을 생각하시면 쉽게 이해할 수 있음 
Nested Loops Join은 선행 테이블에서 읽는 로우수만큼 후행 테이블을 탐색하는 구조임 
만일 선행 테이블에서 100건이 나온다면 후행 테이블을 100번 액세스하게됨
이럴 경우에 후행 테이블에서 대한 읽기 작업의 Starts 값이 "100"이 되는것
E-Rows / E-Bytes / E-Time
예측값
A-Rows / A-Time
실제 실행된 값

 

 

 

 

 

SQL 트레이스

- SQL Trace는 실행되는 SQL문의 실행통계를 세션별로 모아서 Trace 파일을 만듬
- SQL Trace는 세션과 인스턴스 레벨에서 SQL문장들을 분석할 수 있음
- .trc 파일로 저장되고, tkprof 유틸리티를 이용하면 분석

 

 

 

call 커서 상태에 따라 Parse, Execute, Fetch 세 개의 Call로 나누어 각각에 대한 통계정보를 보여준다.
Parse 커서를 파싱하고 실행계획을 생성하는 데 대한 통계
Execute 커서의 실행 단계에 대한 통계
Fetch 레코드를 실제로 Fetch하는데 대한 통계
count Parse, Execute, Fetch 각 단계가 수행된 횟수
cpu 현재 커서가 각 단계를 수행하는 데 소요된 시간
Elapsed Time CPU Time + Wait Time = Response시점 - Call 시점
CPU 시간에 비해 Elapsed 시간이 현저히 큰 경우는 디스크 I/O 때문이다.
disk 디스크로부터 읽은 블록 수
query Consistent 모드에서 읽은 버퍼 블록 수
current Current모드에서 읽은 버퍼 블록 수
rows 각 단계에서 읽거나 갱신한 처리건 수
Misses in library cache during parse Parse 구간에서 해당 SQL을 Library Cache에서 읽지 못하고 잃어버린 횟수
값이 1이면 Hard Parse, 0이면 Soft Parse를 의미함
Optimizer mode 옵티마이저 모드

 

 

 

 

버퍼캐시 히트율

(1 - disk / (query+current)) * 100
또는
( (query+current) -disk) / (query+current) * 100

 

 

 

Row Source Operation 통계 출력

 

 

Rows
각 수행단계에 출력된 row수
Cr
consistent 모드 블록 읽기
Pr(r)
디스크 블록 읽기
Pw(w)
디스크 블록 쓰기
Time
소요시간(us= microsecond)
Cost
Cost Based Optimizer(CBO)에서 예측한 비용
Size
리턴한 데이터 size(bytes)
Card
Cardinality, Cost Based Optimizer(CBO)에서 예측한 row수

 

 

 

 

현재 세션에 SQL 트레이스 거는 방법

 

alter session set sql_trace = true;

select 절;

=> .trc 파일이 생성됨

 

 

 

AutoTrace
- SQL Plus에서 실행계획을 가장 쉽고 빠르게 확인해 볼 수 있는 방법

 

grant plustrace to scott;
- plustrace 권한부여
set autotrace on
- SQL 실제 수행 => SQL 실행결과, 실행계획 및 실행통계 출력
set autotrace on explain
- SQL 실제 수행 => SQL 실행결과, 실행계획 출력
set autotrace on statistics
- SQL 실제 수행 => SQL 실행결과, 실행통계 출력
set autotrace traceonly
- SQL 실제 수행 => 실행계획 및 실행통계 출력
set autotrace traceonly explain
- SQL 실제 수행 X => 실행계획 출력
set autotrace traceonly statistics
- SQL 실제 수행 => 실행통계 출력

 

--------------------------------------------------------------------------------------
| Id  | Operation            | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |         |       1 |      20 |       1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP    |       1 |      20 |       1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN        | PK_EMP |       1 |         |       1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("EMPNO"=7900)

 

 

통계

Statistics
----------------------------------------------------------
      1  recursive calls
      0  db block gets
      2  consistent gets
      0  physical reads
      0  redo size
    596  bytes sent via SQL*Net to client
    523  bytes received via SQL*Net from client
      2  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      1  rows processed

 

recursive calls
사용자의 SQL문을 실행하기 위하여 수행된 SQL 문의 수
db block gets
현재 모드(current mode)에서 버퍼 캐시로부터 읽어온 블록의 총 수
consistent gets
버퍼 캐시의 블록에 대한 일관된 읽기의 요청 횟수. 일관된 읽기는 언두 정보, 즉 롤백 정보에 대한 읽기를 요구할 수도 있으며 이들 언두에 대한 읽기도 계산됨
physical reads
물리적으로 데이터 파일을 읽어 버퍼 캐시에 넣은 횟수
redo size
해당 문이 실행되는 동안 생성된 리두의 전체 크기를 바이트 단위로 나타낸 수
byte sent via SQL*Net to client
서버로부터 클라이언트에 전송된 총 바이트 수
byte recevied via SQL*Net from client
클라이언트로부터 받은 총 바이트 수
SQL*Net roundtrips to/from client
클라이언트로(부터) 전송된 SQL*Net 메시지의 총 수. 다중 행 결과 집합으로부터 꺼내오기 위한 왕복을 포함함
sorts(memory)
사용자의 세션 메모리(정렬 영역)에서 수행된 정렬 sort_area_size 데이터베이스 매개변수에 의해 제어됨
sorts(disk)
사용자의 정렬 영역의 크기를 초과하여 디스크(임시 테이블 영역)를 사용하는 정렬
rows processed
수정되거나 select 문으로부터 반환된 행

 

 

 

 

MS-SQL Server

 

트레이스 확인 방법

set statistics profile on
set statistics io on
set statistics time on

- 구문분석 및 컴파일 시간
- 논리직/물리적 읽기 수
- 각 오퍼레이션 단계별 실행 횟수

 

 

set statistics profile on
- 실제 실행계획 표시
Rows : 각 연산자에서 만든 실제의 행 수를 보여줍니다.
Executes : 각 연산자가 몇 번 실행이 되었는지 보여줍니다
StmtText : 세부적으로 어떤 실행이 있었는지를 보여주거나, 실제로 실행된 쿼리를 보여줍니다
Argument : 수행되는 작업의 추가정보를 제공합니다. TOP 절의 행 수나, Group By 조건, Where 조건, Index Seek 혹은 Scan사항 등

 

set statistics io on
- I/O 통계 표시
테이블 'emp'. 스캔 수 1, 논리적 읽기 28, 실제 읽기 0, 페이지 서버 읽기 0, 미리 읽기 읽기 0, 페이지 서버 미리 읽기 읽기 0, lob 논리적 읽기 0, lob 실제 읽기 0, lob 페이지 서버 읽기 0, lob 미리 읽기 읽기 0, lob 페이지 서버 미리 읽기 읽기 0.

 

set statistics time on
- 시간 통계 표시
SQL Server 구문 분석 및 컴파일 시간: 
   CPU 시간 = 0ms, 경과 시간 = 0ms.

(100개 행이 영향을 받음)

 SQL Server 실행 시간: 
 CPU 시간 = 0ms, 경과 시간 = 222ms

 

 

SET SHOWPLAN_ALL ON
또는
SET SHOWPLAN_TEXT ON
- 예상 실행계획 표시

 

 

 

 

 

쿼리별로 실행계획에 나타나는 형태

SELECT distinct / UNION
HASH (UNIQUE)

GROUP BY
HASH (GROUP BY)

ORDER BY
SORT (ORDER BY)

집계함수 SUM(), COUNT()
SORT (AGGREGATE)

윈도우함수 SUM() over(partition by xx)
WINDOW (SORT)

ROWNUM 사용시
COUNT (STOPKEY)

 

 

Response Time Analysis 방법론
- 세션 또는 시스템 전체에 발생하는 병목 현상과 그 원인을 찾아 문제를 해결하는 방법
- CPU time과 Wait time을 각각 break down하면서 서버의 일량과 대기 시간을 분석

Response Time = Servic Time + Wait Time
또는
Response Time = CPU time + Queue Time

 

 

AWR (Automatic Workload Repository) 보고서
- DB 의 상태를 진단하는 정보
- DBA에게 데이터베이스 실행 시간과 관련된 스냅샷과 상세한 정보를 제공
- Statpack 을 업그레이드 해서 개발 되었다.

 

 

DB 대기 이벤트 및 통계 정보
- 시스템 통계 정보
- 데이터베이스 부하 정보
- SQL 수행 정보
- 활동 세션 정보

 

 

Posted by 헝개
데이터베이스2025. 3. 1. 11:38

SQLP : 제3과목은 SQL 고급활용 및 튜닝 이다.

 

 

3과목. SQL 고급활용 및 튜닝

1. SQL 수행 구조
2. SQL 분석 도구
3. 인덱스 튜닝
4. 조인 튜닝
5. SQL 옵티마이저
6. 고급 SQL 튜닝
7. Lock과 트랜잭션 동시성 제어

 

 

 

 

 

데이터베이스
- 물리적인 OS 파일의 집합 (데이터파일, Redo Log File, Control FIle 등)

인스턴스
- SGA 공유 메모리 영역과 이를 액세스하는 프로세스 집합

SQL 언어 특징
- 구조적 (Structured) 집합적 (Set-based) 선언적 (Declarative) 언어이다.
- 절차적 (Procedural) 언어는 아니다 : PL/SQL, T-SQL에서 구현한다.



백그라운드 프로세스

System Monitor (SMON)
- 인스턴스를 관리, 장애 발생 시 인스턴스 복구하고, 임시 세그먼트와 익스텐트를 모니터링한다.

Process Monitor (PMON)
- 프로세스를 감시, 이상이 생긴 프로세스의 리소스를 복구

Database Writer (DBWn)
- 버퍼캐시에 있는 Dirty 버퍼를 파일에 기록

Log Writer (LGWR)
- Redo 로그 버퍼를 Redo 로그 파일에 기록

Archiver (ARCn)
- Redo 로그가 꽉 차면, Archive Log 파일로 백업한다.

Checkpoint (CKPT)
- 데이터베이스 변경사항인 버퍼를 데이터 파일로 저장하는 것을 보장

Recoverer(RECO)
- 데이터베이스 복구 시 사용




데이터 저장 구조

 

블록(페이지) 데이터 I/O의 최소단위, 블록 단위로 읽어들인다.
익스텐트 테이블 스페이스로부터 공간을 할당받는 단위
논리적으로 인접한 블록의 집합
세그먼트 익스텐트의 집합
하나의 테이블(비파티션 테이블)은 하나의 세그먼트이다.
테이블 세그먼트, 인덱스 세그먼트가 있다.
테이블 스페이스 세그먼트를 담는 컨테이너로, 여러 데이터 파일로 구성된다.




버퍼 블록의 상태

Free 버퍼
- 데이터가 읽히지 않아 비어 있는 상태

DIrty 버퍼
- 버퍼캐시에 적재된 이후 변경이 발생했지만, 데이터파일에 기록되지 않은 상태

Pinned 버퍼
읽기 또는 쓰기 작업을 위해 현재 액세스 되고 있는 버퍼 블록



Undo 로그
- Transaction Rollback 시 사용
- Transaction Recovery
- Read Consistency

Redo 로그
- Database Recovery
- Cache Recovery
- Fast Commit

 



Redo 메커니즘

 

Log Force at commit 트랜잭션의 영속성을 위해, 적어도 커밋 시점에는 Redo 로그를 데이터 파일에 기록한다.
Fast Commit Redo 로그를 믿고, 빠르게 커밋을 완료할 수 있다.
Write Ahead Logging Dirty 블록을 데이터 파일에 기록하기 전에, Redo 엔트리를 Redo 로그 파일에 기록했음이 보장되야 한다.



메모리 영역

SGA (System Global Area)
- 공유 메모리 그룹, 모든 서버 및 백그라운드 프로스세에 공유된다.
- Shared Pool, 데이터베이스 버퍼캐시, Redo 로그 버퍼, 딕셔너리 캐시

PGA (Program Global Area)
- 비공유 메모리 구조 그룹
- 한 프로세스 혹은 스레드의 개별적인 메모리 공간
- 정렬 공간 (Sort Area), 세션 정보 (Session Information), 커서 상태 정보(Cursor State), 변수 저장 공간 (Stack Area)

UGA (User Global Area)
- 로그온 정보나 세션이 필요로 하는 정보를 저장하고 있는 세션 메모리
- Shared Server의 경우 SGA 에 존재
- Dedicated Server일 경우  PGA 내부에 존재

CGA (Call Global Area)
- 데이터베이스 콜(parse, execute, fetch)에 필요하고, 끝나면 해제되는 데이터를 담고 있다.



Single Block I/O
- 기본적인 인덱스 사용 시 단일 블록으로 읽어들인다.

Multi Block I/O
- 인접한 블록을 함께 읽어 들인다.
- 한 익스텐트 경계를 넘지는 못한다.
- Index Fast Full Scan, Table Full Scan 에서 다중 블록으로 읽어들인다.

Direct Path I/O 는 버퍼 캐시를 경유하지 않는다.
(병렬 프로세스로 테이블을 Full Scan)

 


LRU 알고리즘
- 가장 오래전에 사용된 데이터 블록을 버퍼캐시에서 밀어내는 알고리즘
- 사용빈도가 높은 데이터 블록이 버퍼캐시에 오래 남아 있도록 한다.


Shared Pool

빠른 Parsing을 위해 Hard Parsing을 줄이고 Soft Parsing의 비율을 높여 Oracle Database의 성능을 높히는 것

Soft Parsing

- Library Cache에 저장되어 있는 SQL문을 수행하여 Parse 작업없이 기존에 있는 실행계획대로 실행

 

Hard Parsing

- Parsing 작업을 수행하여 SQL문 확인 및 실행계획을 새로 생성함

 


Library Cache

- Parsing 정보를 저장하는 메모리

- Oracle Instance SGA Shared Pool 영역에 존재
- SQL 과 실행계획, PL/SQL 프로그램(프로시저, 함수, 트리거 등) 캐싱

 

Data Dictionary Cache

- Parsing에 필요한 Data Dictionary 정보를 저장하는 메모리


Result Cache

- Parsing 없이 결과를 확인할 수 있도록 Query의 결과값만 저장하는 메모리


Reserved Pool

- Parsing 크기가 큰 데이터를 저장하는 메모리

 

 

Posted by 헝개