# 요약 | |
■ TX LOCK 1~2. enq : TX - row lock contention 3. enq : TX - allocate itl entry 테이블 재구성 alter table 테이블 move 인덱스 재구성 alter index 인덱스 rebuild online; ■ Full Table Scan ■ Rowid Scan ■ Index Scan 1. Index Range Scan 2. Index Unique Scan 3. Inlist Iterator 4. Composite Index 조합 인덱스 5. Index Full Scan select /*+ 내용 */ 힌트 set autot traceonly exp 실행계획 ON set autot off 실행계획 OFF |
■ TX LOCK
1. 특정 행을 변경하고자 하는 경우 enq : TX - row lock contention |
|
HR1> HR@ora11g> drop table hr.emp purge; Table dropped. HR@ora11g> create table hr.emp as select * from hr.employees; Table created. HR@ora11g> select employee_id, salary from hr.emp where employee_id = 200; EMPLOYEE_ID SALARY ----------- ---------- 200 4400 HR@ora11g> update hr.emp set salary = 2000 where employee_id = 200; 1 row updated. HR2> HR@ora11g> delete from hr.emp where employee_id = 200; wait... SYS> # 이벤트 발생 확인 SYS@ora11g> select sid,serial#,username,blocking_session,event,sql_id,prev_sql_id from v$session where event like '%TX%'; SID SERIAL# USERNAME BLOCKING_SESSION EVENT SQL_ID PREV_SQL_ID ------- ---------- ---------- ---------------- ----------------------------------- ------------- ------------- 26 591 HR 27 enq: TX - row lock contention 8suw0gu1fumpv 70aq84vtjsk4r - EVENT like '%TX%' 또는 '%enq%' # 이벤트가 발생하여 대기중인 SQL문 확인 SYS@ora11g> select sql_text from v$sql where sql_id ='8suw0gu1fumpv'; SQL_TEXT -------------------------------------------------- delete from hr.emp where employee_id = 200 # 이벤트를 발생시킨 원인 확인 SYS@ora11g> select sid,serial#,username,blocking_session,sql_id,prev_sql_id from v$session where sid in (select blocking_session from v$session); SID SERIAL# USERNAME BLOCKING_SESSION SQL_ID PREV_SQL_ID ------- ---------- ---------- ---------------- ------------- ------------- 27 923 HR 0xwwv6ba9d01c # 이벤트를 발생시킨 SQL문 확인 SYS@ora11g> select sql_text from v$sql where sql_id ='0xwwv6ba9d01c'; SQL_TEXT -------------------------------------------------- update hr.emp set salary = 2000 where employee_id = 200 # TX, TM 타입의 LOCK 정보 확인 SYS@ora11g> select * from v$lock where sid in (26,27) and type in ('TX','TM'); ADDR KADDR SID TYPE ID1 ID2 LMODE REQUEST CTIME BLOCK -------- -------- ------- ------- ---------- ---------- ---------- ---------- ---------- ---------- 4956C3DC 4956C408 26 TX 327710 2979 0 6 647 0 F6CC25E8 F6CC2618 26 TM 88739 0 3 0 647 0 F6CC25E8 F6CC2618 27 TM 88739 0 3 0 664 0 F6CC25E8 F6CC2618 27 TM 5187 0 3 0 664 0 48F4685C 48F4689C 27 TX 327710 2979 6 0 664 1 - LMODE 3 : Shared mode에서도 lock을 금지 6 : exclusive. query는 허용, 같은 테이블에서의 다른 작업은 금지. - REQUEST 6 : Lock 요청 중 - BLOCK 1 : 다른 Lock을 블로킹 중 - TYPE : TM ID1 : object ID - TYPE : TX ID1 : undo segment 번호 + transaction slot 번호 ID2 : transaction slot sequence # SYS@ora11g> select sid, type, id1, id2, lmode, request, block, to_char(trunc(id1/power(2,16))) usn, bitand(id1, to_number('ffff','xxxx'))+0 slot, id2 sqn from v$lock where sid in (26,27) and type in ('TX','TM'); SID TYPE ID1 ID2 LMODE REQUEST BLOCK USN SLOT SQN ------- ------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- 26 TX 327710 2979 0 6 0 5 30 2979 26 TM 88739 0 3 0 0 1 23203 0 27 TM 88739 0 3 0 0 1 23203 0 27 TM 5187 0 3 0 0 0 5187 0 27 TX 327710 2979 6 0 1 5 30 2979 - USN : Undo Segmanet No - SLOT : Transaction Slot No - SQN : Transaction Slot Sequence# # Undo 정보 SYS@ora11g> select segment_name from dba_rollback_segs where segment_id = 5; SEGMENT_NAME ----------------------- _SYSSMU5_784711609$ # ID1 = OBJECT_ID 객체찾기 SYS@ora11g> select object_name, object_type, data_object_id, object_id from dba_objects where object_id = 88739; OBJECT_NAME OBJECT_TYPE DATA_OBJECT_ID OBJECT_ID -------------------- ------------------- -------------- ---------- EMP TABLE 88739 88739 - OBJECT_ID 최초 생성번호 - DATA_OBJECT_ID 현 변호, rowid에서 사용 # 대기 중인 행 정보 SYS@ora11g> select sid,serial#,username,blocking_session,event, row_wait_obj# as obj_no, row_wait_file# as file_no, row_wait_block# as block_no, row_wait_row# as row_no from v$session where event like '%TX%'; SID SERIAL# USERNAME BLOCKING_SESSION EVENT OBJ_NO FILE_NO BLOCK_NO ROW_NO ------- --------- ---------- ---------------- ------------------------------ ------- ------- -------- ------- 26 591 HR 27 enq: TX - row lock contention 88739 4 147 2 > 충돌된 행 정보 찾기 4개 컬럼 (10g) -> 조함하여 row id 생성 가능 SYS@ora11g> select tablespace_name, file_name from dba_data_files where file_id = 4; TABLESPACE_NAME FILE_NAME --------------- -------------------------------------------------- USERS /u01/app/oracle/oradata/ora11g/users01.dbf SYS@ora11g> select dbms_rowid.rowid_create(0, 88739, 4, 147, 2) as "RESTRICTED ROWID", dbms_rowid.rowid_create(1, 88739, 4, 147, 2) as "EXTENDED ROWID" from dual; RESTRICTED ROWID EXTENDED ROWID ------------------ ------------------ 00000093.0002.0004 AAAVqjAAEAAAACTAAC restricted rowid(v7) 제한된 rowid, 6byte : #block.#rowslot.#file extended rowid(v8) 확장된 rowid, 10byte : #object(6)+#file(3)+#block(6)+#rowslot(3) SYS@ora11g> select employee_id, salary from hr.emp where rowid = 'AAAVqjAAEAAAACTAAC'; EMPLOYEE_ID SALARY ----------- ---------- 200 4400 HR1> HR@ora11g> rollback; Rollback complete. HR2> 1 row deleted. HR@ora11g> rollback; Rollback complete. HR1> HR@ora11g> delete from hr.emp where employee_id = 200; > emp 테이블에 대해 TM Lock type이 걸리고 Lock mode RX, SX, 3 걸린다. = 데이터 갱신, 입력, 삭제 Lock > transaction 대상 행에 대해서는 TX Lock type이 걸리고 Lock mode X, 6 걸린다. = query는 허용, 다른 작업 금지 HR2> HR@ora11g> drop table hr.emp purge; ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired > emp 테이블에 대해 TM Lock type이면서 Lock mode RX, SX, 3 걸려 있어서 오류 발생 HR1> HR@ora11g> rollback; Rollback complete. HR2> HR@ora11g> drop table hr.emp purge; |
2. 특정 행에서 unique key, primary key에 해당하는 데이터를 입력, 수정하는 경우 enq : TX - row lock contention |
|
HR@ora11g> create table hr.unique_test(id number); Table created. HR@ora11g> create unique index hr.unique_test_idx on hr.unique_test(id); Index created. HR1> HR@ora11g> insert into hr.unique_test(id) values(1); 1 row created. HR2> HR@ora11g> insert into hr.unique_test(id) values(1); wait... > 중복데이터, 오류안나고 대기?? > HR1에서 같은 작업중이니 기다려라 > transaction에 해당하는 행에 대해서 TX Lock type이면서 lmode는 X, 6 수행이 되면서 동일한 값이 다른 세션에서 입력이 된 경우 REQUEST S(Share), 4 로 대기 한다. SYS> SYS@ora11g> select sid,serial#,username,blocking_session,event,sql_id,prev_sql_id from v$session where event like '%TX%'; SID SERIAL# USERNAME BLOCKING_SESSION EVENT SQL_ID ------- ---------- ---------- ---------------- --------------------------------------------- ------------- PREV_SQL_ID ------------- 26 591 HR 27 enq: TX - row lock contention cuz65hgum0qns c84ynr1u8hbs2 SYS@ora11g> select * from v$lock where sid in (26,27) and type in ('TX','TM'); ADDR KADDR SID TYPE ID1 ID2 LMODE REQUEST CTIME BLOCK -------- -------- ------- ---- ---------- ---------- ---------- ---------- ---------- ---------- 4956BC98 4956BCC4 26 TX 393232 3231 0 4 188 0 F6F89BB8 F6F89BE8 26 TM 88719 0 3 0 188 0 F6F89BB8 F6F89BE8 27 TM 88719 0 3 0 188 0 48F21538 48F21578 26 TX 458772 2248 6 0 188 0 48F792AC 48F792EC 27 TX 393232 3231 6 0 188 1 HR1> HR@ora11g> commit; HR2> ORA-00001: unique constraint (HR.UNIQUE_TEST_IDX) violated > 만약 HR1에서 rollback을 수행한 경우에는 HR2에서 insert가 수행된다. |
3. 변경하고자하는 블록의 ITL에 자신에 해당하는 트랜잭션 엔트리(slot)를 등록하고자하는 경우 enq : TX - allocate ITL entry |
|
SYS> SYS@ora11g> create table hr.itl_table(id number,l_name varchar2(1000), f_name varchar2(1000)) initrans 1 maxtrans 2 pctfree 0; Table created. - initrans : block header에 transaction layer에 생성된 transaction slot의 수 - maxtrans : data가 저장되어있는 free 공간에 생성될 수 있는 최대 transaction slot의 수 필요할 때 free 공간이 있으면 생성해서 사용하다가 transaction이 종료되면 자동으로 해제된다. - pctfree : 최초로 블록에 데이터가 저장될 때 pctfree 값(%)을 제외 시키고 입력한다. 1. 기존의 행의 값들의 증가분때문에 남겨 놓은 free 영역 만약에 free 공간이 없으면 다른 블록이 이전을 해야하는 문제가 발생 row migration이 발생 할 때 2. maxtrans 값을 보장하기 위해서 만약에 transaction slot을 생성하지 못하면 트랜잭션을 수행한 세션에서는 대기하는 일이 발생한다. enq : TX - allocate ITL entry SYS@ora11g> select ini_trans, max_trans, pct_free from dba_tables where table_name = 'ITL_TABLE'; INI_TRANS MAX_TRANS PCT_FREE ---------- ---------- ---------- 1 255 0 SYS@ora11g> insert into hr.itl_table(id,l_name,f_name) select level, rpad('x',1000,'x'), rpad('z',1000,'z') from dual connect by level <= 10; 10 rows created. SYS@ora11g> commit; Commit complete. SYS@ora11g> select id, rowid, dbms_rowid.rowid_block_number(rowid) from hr.itl_table order by 3; ID ROWID DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) ---------- ------------------ ------------------------------------ 5 AAAVqRAAEAAAEXLAAA 17867 6 AAAVqRAAEAAAEXLAAB 17867 7 AAAVqRAAEAAAEXLAAC 17867 8 AAAVqRAAEAAAEXLAAD 17867 9 AAAVqRAAEAAAEXOAAA 17870 10 AAAVqRAAEAAAEXOAAB 17870 1 AAAVqRAAEAAAEXPAAA 17871 2 AAAVqRAAEAAAEXPAAB 17871 3 AAAVqRAAEAAAEXPAAC 17871 4 AAAVqRAAEAAAEXPAAD 17871 HR1> HR@ora11g> update hr.itl_table set l_name = rpad('y',1000,'y'), f_name = rpad('a',1000,'a') where id = 5; 1 row updated. > initrans 1 잡아 사용 HR2> HR@ora11g> update hr.itl_table set l_name = rpad('y',1000,'y'), f_name = rpad('a',1000,'a') where id = 6; 1 row updated. > pctfree 공간 사용 HR3> HR@ora11g> update hr.itl_table set l_name = rpad('y',1000,'y'), f_name = rpad('a',1000,'a') where id = 7; wait... > 왜 못하고 있지? SYS> SYS@ora11g> select sid,serial#,username,blocking_session,event,sql_id,prev_sql_id from v$session where event like '%TX%'; SID SERIAL# USERNAME BLOCKING_SESSION EVENT SQL_ID ------- ---------- ---------- ---------------- --------------------------------------------- ------------- PREV_SQL_ID ------------- 23 48333 HR 27 enq: TX - allocate ITL entry 8up8cm5r9rghs dyk4dprp70d74 > HR2 때문에 HR3 대기 중 SYS@ora11g> select sid,serial#,username,blocking_session,sql_id,prev_sql_id from v$session where sid in (select blocking_session from v$session); SID SERIAL# USERNAME BLOCKING_SESSION SQL_ID PREV_SQL_ID ------- ---------- ---------- ---------------- ------------- ------------- 27 923 HR 3635umwnmxj1s > 문제 원인 SYS@ora11g> select sql_text from v$sql where sql_id ='3635umwnmxj1s'; SQL_TEXT -------------------------------------------------- update hr.itl_table set l_name = rpad('y',1000,'y' ), f_name = rpad('a',1000,'a') where id = 5 HR1> + HR2> + HR3> HR@ora11g> rollback; Rollback complete. SYS> SYS@ora11g> select ini_trans, max_trans, pct_free from dba_tables where table_name = 'ITL_TABLE'; INI_TRANS MAX_TRANS PCT_FREE ---------- ---------- ---------- 1 255 0 >> enq: TX - allocate ITL entry 해결 방안 # 테이블 재구성 SYS@ora11g> alter table hr.itl_table initrans 2 pctfree 10; Table altered. > block parameter 값을 수정한 경우 새로운 block부터 적용된다. SYS@ora11g> select ini_trans, max_trans, pct_free from dba_tables where table_name = 'ITL_TABLE'; INI_TRANS MAX_TRANS PCT_FREE ---------- ---------- ---------- 2 255 10 # 기존 block에도 새로운 block parameter를 적용하려면 테이블을 재구성해야한다. SYS@ora11g> alter table hr.itl_table move; Table altered. # 테이블을 재구성하면 기존 행들의 rowid가 바뀐다. 인덱스는 꼭 재구성해야한다. SYS@ora11g> alter index hr.itl_table_id rebuild online; HR1> HR@ora11g> update hr.itl_table set l_name = rpad('y',1000,'y'), f_name = rpad('a',1000,'a') where id = 5; 1 row updated. HR2> HR@ora11g> update hr.itl_table set l_name = rpad('y',1000,'y'), f_name = rpad('a',1000,'a') where id = 6; 1 row updated. HR3> HR@ora11g> update hr.itl_table set l_name = rpad('y',1000,'y'), f_name = rpad('a',1000,'a') where id = 7; 1 row updated. |
■ Full Table Scan
- 많은 양의 데이터 검색시 유용함
- 첫번째 블록부터 마지막 사용한 블록(High Water Mark)까지 읽어오는 방식
- Multi Block I/O 발생한다.
- 한번에 I/O call 방식할 때 db_file_multiblock_read_count 설정되어있는 블록 수만큼 읽어 들이기 위해서는
extent 안에 db_file_multiblock_read_count 설정되어있는 갯수만큼 있을 경우에 나름 Multi Block I/O 성능이
좋을 수 있다.
- db file scattered read wait event 발생할 수 있다.
full table scan으로 인해서 과도한 multi block i/o 발생 시에 나온다.
SYS@ora11g> show parameter db_file_multiblock_read_count NAME TYPE VALUE ------------------------------------ ------- ------------------------------ db_file_multiblock_read_count integer 81 HR> HR@ora11g> create table hr.emp as select * from hr.employees; # 실행계획 보기 HR@ora11g> set autot traceonly exp HR@ora11g> select * from hr.emp where employee_id = 100; Execution Plan ---------------------------------------------------------- Plan hash value: 3956160932 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 133 | 3 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| EMP | 1 | 133 | 3 (0)| 00:00:01 | -------------------------------------------------------------------------- ㄴ * 표시가 있다면 밑에 정보 확인 Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("EMPLOYEE_ID"=100) ㄴ filter 방식이면 Full Scan Note ----- - dynamic sampling used for this statement (level=2) # full table scan 속도를 개선 1. 병렬 작업(parallel 또는 parallel_index 힌트를 사용) -> 많이 사용 데이터 버퍼캐시를 덜 사용 = 다이렉트 2. db_file_multiblock_read_count 파리미터의 값을 조정하자 alter session set db_file_multiblock_read_count = 128; select /*+ full(e) parallel(e,2) */ * from emp e; 이 테이블을 무조건 풀스캔, 프로세서 2개 띄어서 읽자. 인덱스 못하게 full parallel 같이 사용 |
■ rowid scan
- user rowid, index rowid를 이용하여 소량의 데이터 검색 시 유용하다.
- single block I/O 수행
- DB file sequential read wait event 발생할 수 있다.
select employee_id, rowid, dbms_rowid.rowid_object(rowid) as object_id, dbms_rowid.rowid_relative_fno(rowid) as file_no, dbms_rowid.rowid_block_number(rowid) as block_no, dbms_rowid.rowid_row_number(rowid) as row_slot_no from hr.emp; EMPLOYEE_ID ROWID OBJECT_ID FILE_NO BLOCK_NO ROW_SLOT_NO ----------- ------------------ ---------- ---------- ---------- ----------- 198 AAAVqdAAEAAAEXLAAA 88733 4 17867 0 199 AAAVqdAAEAAAEXLAAB 88733 4 17867 1 200 AAAVqdAAEAAAEXLAAC 88733 4 17867 2 201 AAAVqdAAEAAAEXLAAD 88733 4 17867 3 202 AAAVqdAAEAAAEXLAAE 88733 4 17867 4 203 AAAVqdAAEAAAEXLAAF 88733 4 17867 5 204 AAAVqdAAEAAAEXLAAG 88733 4 17867 6 205 AAAVqdAAEAAAEXLAAH 88733 4 17867 7 206 AAAVqdAAEAAAEXLAAI 88733 4 17867 8 100 AAAVqdAAEAAAEXLAAJ 88733 4 17867 9 ... 188 AAAVqdAAEAAAEXLABh 88733 4 17867 97 189 AAAVqdAAEAAAEXMAAA 88733 4 17868 0 190 AAAVqdAAEAAAEXMAAB 88733 4 17868 1 191 AAAVqdAAEAAAEXMAAC 88733 4 17868 2 SYS@ora11g> set autot traceonly exp HR@ora11g> select * from hr.emp where employee_id = 100; Execution Plan ---------------------------------------------------------- Plan hash value: 3956160932 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 133 | 3 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| EMP | 1 | 133 | 3 (0)| 00:00:01 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("EMPLOYEE_ID"=100) Note ----- - dynamic sampling used for this statement (level=2) >> 가장 빠르지만 rowid 알기 쉽지 않다 |
■ index scan
1. index range scan
인덱스 root block -> branch block -> leaf block 까지 수직으로 탐색한 후
leaf block에서 필요한 범위만 스캔하는 방식(one plus one scan)
HR@ora11g> create index hr.emp_idx on hr.emp(employee_id); HR@ora11g> select ix.index_name, ix.uniqueness, ic.column_name from user_indexes ix, user_ind_columns ic where ix.index_name = ic.index_name and ix.table_name = 'EMP'; INDEX_NAME UNIQUENES COLUMN_NAME ------------------------------ --------- -------------------- EMP_IDX NONUNIQUE EMPLOYEE_ID HR@ora11g> set autot traceonly exp HR@ora11g> select * from hr.emp where employee_id = 100; Execution Plan ---------------------------------------------------------- Plan hash value: 1472992808 --------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 133 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 133 | 2 (0)| 00:00:01 | I/O |* 2 | INDEX RANGE SCAN | EMP_IDX | 1 | | 1 (0)| 00:00:01 | 인덱스 스캔 --------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("EMPLOYEE_ID"=100) ㄴ access : 해당 데이터의 block이 어디에 있는지 안다. Note ----- - dynamic sampling used for this statement (level=2) > NONUNIQUE이면 INDEX RANGE SCAN one plus one scan 발생하면 느려지니 buffer pinning (latch 점유, i/o 덜 발생) # 실행계획 보기 종료 HR@ora11g> set autot off |
|
HR@ora11g> set autot traceonly exp HR@ora11g> select /*+ full(e) */ * from hr.emp e where employee_id = 100; Execution Plan ---------------------------------------------------------- Plan hash value: 3956160932 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 133 | 3 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| EMP | 1 | 133 | 3 (0)| 00:00:01 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("EMPLOYEE_ID"=100) Note ----- - dynamic sampling used for this statement (level=2) |
|
HR@ora11g> select * from hr.emp where to_number(employee_id) = 100; Execution Plan ---------------------------------------------------------- Plan hash value: 3956160932 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 133 | 3 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| EMP | 1 | 133 | 3 (0)| 00:00:01 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(TO_NUMBER(TO_CHAR("EMPLOYEE_ID"))=100) Note ----- - dynamic sampling used for this statement (level=2) >> 1건 찾는데 풀스캔. 성능 저하 |
|
HR@ora11g> select /*+ index(e emp_idx) */ * from hr.emp e where employee_id = 100; Execution Plan ---------------------------------------------------------- Plan hash value: 1472992808 --------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 133 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 133 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | EMP_IDX | 1 | | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("EMPLOYEE_ID"=100) Note ----- - dynamic sampling used for this statement (level=2) |
|
# INDEX RANGE SCAN 유도 |
|
HR@ora11g> select /*+ index_rs(e emp_idx) */ * from hr.emp e where employee_id = 100; Execution Plan ---------------------------------------------------------- Plan hash value: 1472992808 --------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 133 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 133 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | EMP_IDX | 1 | | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("EMPLOYEE_ID"=100) Note ----- - dynamic sampling used for this statement (level=2) |
/*+ */ : 힌트
HR@ora11g> drop index hr.emp_idx; Index dropped. HR@ora11g> create unique index hr.emp_idx on hr.emp(employee_id); Index created. HR@ora11g> alter table hr.emp add constraint emp_id_pk primary key(employee_id) using index hr.emp_idx; Table altered. HR@ora11g> select ix.index_name, ix.uniqueness, ic.column_name from user_indexes ix, user_ind_columns ic where ix.index_name = ic.index_name and ix.table_name = 'EMP'; INDEX_NAME UNIQUENES COLUMN_NAME ------------------------------ --------- -------------------- EMP_IDX UNIQUE EMPLOYEE_ID select c.column_name, u.constraint_name, u.constraint_type, u.search_condition, u.index_name from user_constraints u, user_cons_columns c where u.constraint_name = c.constraint_name and u.table_name = 'EMP'; COLUMN_NAME CONSTRAINT_NAME C SEARCH_CONDITION INDEX_NAME -------------------- ------------------------------ - ------------------------- ------------------------------ LAST_NAME SYS_C0011602 C "LAST_NAME" IS NOT NULL EMAIL SYS_C0011603 C "EMAIL" IS NOT NULL HIRE_DATE SYS_C0011604 C "HIRE_DATE" IS NOT NULL JOB_ID SYS_C0011605 C "JOB_ID" IS NOT NULL EMPLOYEE_ID EMP_ID_PK P EMP_IDX |
|
2. index unique scan - 컬럼에 유일한 값으로 인덱스가 생성된 경우 사용된다. - 비교연산자는 = 사용할때만 사용된다. - unique index라고 하더라도 범위스캔을 수행할 경우 index range scan 으로 수행된다. |
|
HR@ora11g> set autot traceonly exp HR@ora11g> select * from hr.emp where employee_id = 100; Execution Plan ---------------------------------------------------------- Plan hash value: 2466118986 --------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 133 | 1 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 133 | 1 (0)| 00:00:01 | |* 2 | INDEX UNIQUE SCAN | EMP_IDX | 1 | | 0 (0)| 00:00:01 | --------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("EMPLOYEE_ID"=100) |
3. Inlist Iterator : 내부적으로 select문을 분리
HR@ora11g> select * from hr.emp where employee_id in (100,200); Execution Plan ---------------------------------------------------------- Plan hash value: 1651504986 ---------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 266 | 2 (0)| 00:00:01 | | 1 | INLIST ITERATOR | | | | | | | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 2 | 266 | 2 (0)| 00:00:01 | |* 3 | INDEX UNIQUE SCAN | EMP_IDX | 1 | | 1 (0)| 00:00:01 | ---------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("EMPLOYEE_ID"=100 OR "EMPLOYEE_ID"=200) ㄴ select문이 2개, 내부적으로 select문을 분리 Note ----- - dynamic sampling used for this statement (level=2) |
|
HR@ora11g> select * from hr.emp where employee_id = 100 union all select * from hr.emp where employee_id = 200; Execution Plan ---------------------------------------------------------- Plan hash value: 1345714510 ---------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 266 | 2 (0)| 00:00:01 | | 1 | UNION-ALL | | | | | | | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 133 | 1 (0)| 00:00:01 | |* 3 | INDEX UNIQUE SCAN | EMP_IDX | 1 | | 0 (0)| 00:00:01 | | 4 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 133 | 1 (0)| 00:00:01 | |* 5 | INDEX UNIQUE SCAN | EMP_IDX | 1 | | 0 (0)| 00:00:01 | ---------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("EMPLOYEE_ID"=100) 5 - access("EMPLOYEE_ID"=200) |
|
HR@ora11g> select * from hr.emp where employee_id = 100 union select * from hr.emp where employee_id = 200; Execution Plan ---------------------------------------------------------- Plan hash value: 28680880 ----------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 266 | 4 (50)| 00:00:01 | | 1 | SORT UNIQUE | | 2 | 266 | 4 (50)| 00:00:01 | | 2 | UNION-ALL | | | | | | | 3 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 133 | 1 (0)| 00:00:01 | |* 4 | INDEX UNIQUE SCAN | EMP_IDX | 1 | | 0 (0)| 00:00:01 | | 5 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 133 | 1 (0)| 00:00:01 | |* 6 | INDEX UNIQUE SCAN | EMP_IDX | 1 | | 0 (0)| 00:00:01 | ----------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("EMPLOYEE_ID"=100) 6 - access("EMPLOYEE_ID"=200) |
|
HR@ora11g> create index hr.emp_name_idx on hr.emp(last_name); Index created. HR@ora11g> select ix.index_name, ix.uniqueness, ic.column_name from user_indexes ix, user_ind_columns ic where ix.index_name = ic.index_name and ix.table_name = 'EMP'; INDEX_NAME UNIQUENES COLUMN_NAME ------------------------------ --------- -------------------- EMP_IDX UNIQUE EMPLOYEE_ID EMP_NAME_IDX NONUNIQUE LAST_NAME HR@ora11g> select * from hr.emp where last_name = 'King'; Execution Plan ---------------------------------------------------------- Plan hash value: 2039216739 -------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 266 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 2 | 266 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | EMP_NAME_IDX | 2 | | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("LAST_NAME"='King') Note ----- - dynamic sampling used for this statement (level=2) |
|
HR@ora11g> select * from hr.emp where last_name = 'King' and first_name = 'Steven'; Execution Plan ---------------------------------------------------------- Plan hash value: 2039216739 -------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 133 | 2 (0)| 00:00:01 | |* 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 133 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | EMP_NAME_IDX | 2 | | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("FIRST_NAME"='Steven') -> 2 - access("LAST_NAME"='King') Note ----- - dynamic sampling used for this statement (level=2) > 인덱스가 걸린 컬럼 and 인덱스 없는 컬럼의 문제점 first_name은 인덱스 없어서 full scan 발생 > 자주 묶이는 칼럼이라면 차라리 조합 인덱스를 만들자 |
4. 조합 인덱스 - where절에 자주 사용되는 칼럼들을 하나의 인덱스 생성 - 선행 컬럼을 잘 만들어야 한다. 기준 우선순위 : 혼자서도 자주 사용되는 컬럼 범위를 줄일 수 있는 컬럼 |
|
HR@ora11g> drop index hr.emp_name_idx; Index dropped. HR@ora11g> create index hr.emp_name_idx on hr.emp(last_name, first_name); Index created. HR@ora11g> select ix.index_name, ix.uniqueness, ic.column_name, ic.column_position from user_indexes ix, user_ind_columns ic where ix.index_name = ic.index_name and ix.table_name = 'EMP'; INDEX_NAME UNIQUENES COLUMN_NAME COLUMN_POSITION ------------------------------ --------- -------------------- --------------- EMP_IDX UNIQUE EMPLOYEE_ID 1 EMP_NAME_IDX NONUNIQUE FIRST_NAME 2 EMP_NAME_IDX NONUNIQUE LAST_NAME 1 HR@ora11g> select * from hr.emp where last_name = 'King' and first_name = 'Steven'; Execution Plan ---------------------------------------------------------- Plan hash value: 2039216739 -------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 133 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 133 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | EMP_NAME_IDX | 1 | | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("LAST_NAME"='King' AND "FIRST_NAME"='Steven') Note ----- - dynamic sampling used for this statement (level=2) |
5. index full scan | |
HR@ora11g> select last_name, first_name from hr.emp; Execution Plan ---------------------------------------------------------- Plan hash value: 1448724716 --------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 107 | 2782 | 1 (0)| 00:00:01 | | 1 | INDEX FULL SCAN | EMP_NAME_IDX | 107 | 2782 | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------- Note ----- - dynamic sampling used for this statement (level=2) HR@ora11g> select count(*) from hr.emp; Execution Plan ---------------------------------------------------------- Plan hash value: 660937672 -------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | -------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 1 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | INDEX FULL SCAN| EMP_IDX | 107 | 1 (0)| 00:00:01 | -------------------------------------------------------------------- Note ----- - dynamic sampling used for this statement (level=2) - SORT AGGREGATE : 그룹함수를 사용했구나 select c.column_name, u.constraint_name, u.constraint_type, u.search_condition, u.index_name from user_constraints u, user_cons_columns c where u.constraint_name = c.constraint_name and u.table_name = 'EMP'; COLUMN_NAME CONSTRAINT_NAME C SEARCH_CONDITION INDEX_NAME -------------------- ------------------------------ - ------------------------- -------------------- LAST_NAME SYS_C0011602 C "LAST_NAME" IS NOT NULL EMAIL SYS_C0011603 C "EMAIL" IS NOT NULL HIRE_DATE SYS_C0011604 C "HIRE_DATE" IS NOT NULL JOB_ID SYS_C0011605 C "JOB_ID" IS NOT NULL EMPLOYEE_ID EMP_ID_PK P EMP_IDX |
6. index fast full scan | |
HR@ora11g> select /*+ index_ffs(e emp_idx) */ count(*) from hr.emp e; Execution Plan ---------------------------------------------------------- Plan hash value: 3644266057 ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | INDEX FAST FULL SCAN| EMP_IDX | 107 | 2 (0)| 00:00:01 | ------------------------------------------------------------------------- Note ----- - dynamic sampling used for this statement (level=2) # index full scan - single block i/o - 정렬 보장 - 속도 느림 - DB file sequential read # index fast full scan - multi block i/o - 정렬 안됨 - 속도 빠름 - db file scatteted read # index full scan보다 index fast full scan이 더 빠른 이유? - multi block i/o db_file_multiblock_read_count - 병령처리가 가능 HR@ora11g> select /*+ index_ffs(e emp_idx) [parallel_index(e,emp_idx,2) */ count(*) from hr.emp e; Execution Plan ---------------------------------------------------------- Plan hash value: 3644266057 ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | INDEX FAST FULL SCAN| EMP_IDX | 107 | 2 (0)| 00:00:01 | ------------------------------------------------------------------------- Note ----- - dynamic sampling used for this statement (level=2) |
'Oracle 11g > Tuning' 카테고리의 다른 글
84 Serial direct read, Cluster Factor (0) | 2024.02.14 |
---|---|
83 Explain Plan, AutoTrace, v$sql_plan, Buffer Pinning, SQL TRACE (0) | 2024.02.13 |
81 Trans, Lock, Redo, Append Hint (1) | 2024.02.07 |
80 Data Buffer Cache, Buffer Busy Wait, LRU, LRUW (1) | 2024.02.06 |
79 Shared Pool, Library Cache (1) | 2024.02.05 |