본문 바로가기
🗄️ DB_이야기/# ⚡SQL

[Oracle] SQL 분석 도구

by gwon_s 2026. 4. 27.

👇테스트 데이터 만들기

더보기

-- PDB로 세션 전환

SQL> ALTER SESSION SET CONTAINER = PDB;

 

-- 유저 생성

SQL> CREATE USER SQLP IDENTIFIED BY SQLP;

 

-- 접속 및 리소스 사용 권한 부여

SQL> GRANT CONNECT, RESOURCE, DBA TO SQLP;

 

-- 테이블스페이스 할당량 제한 해제

SQL> ALTER USER SQLP QUOTA UNLIMITED ON USERS;

 

-- SQLP 유저로 PDB 접속하기

SQL> CONN SQLP/SQLP@LOCALHOST:1521@PDB

 

 -- 테이블 생성 및 데이터 삽입

-- 부서 테이블 생성
SQL> CREATE TABLE DEPT (
	DEPTNO NUMBER(10) PRIMARY KEY,
    DNAME  VARCHAR2(50),
    LOC    VARCHAR2(50)
);

SQL> INSERT INTO DEPT_BIG (DEPTNO, DNAME, LOC)
SELECT 
    LEVEL AS DEPTNO,
    'Department_' || LEVEL AS DNAME,
    'Location_' || MOD(LEVEL, 5) AS LOC
FROM DUAL
CONNECT BY LEVEL <= 100;
COMMIT;
-- 사원 테이블 생성
SQL> CREATE TABLE EMP_BIG AS
SELECT 
    	LEVEL AS EMPNO,
    	DBMS_RANDOM.STRING('U', 10) AS ENAME,
    	'JOB_' || MOD(LEVEL, 5) AS JOB,
    	FLOOR(DBMS_RANDOM.VALUE(7000, 9999)) AS MGR,
    	TO_DATE('2020-01-01', 'YYYY-MM-DD') + MOD(LEVEL, 2000) AS HIREDATE,
    	DBMS_RANDOM.VALUE(1000, 5000) AS SAL,
    	DBMS_RANDOM.VALUE(0, 1000) AS COMM,
    	MOD(LEVEL, 10) + 1 AS DEPTNO
FROM DUAL
CONNECT BY LEVEL <= 1000000;

 

-- 인덱스 생성

SQL> CREATE INDEX IX_EMP_BIG_SAL ON EMP(SAL);

 

-- 통계 정보 수집

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME => 'SQLP', TABNAME => 'EMP');

 


실행 계획 확인 방법

  1. EXPLAIN PLAN
  2. AUTOTRACE
  3. SQLTRACE
  4. TKPROF

1. EXPLAIN PLAN (DBMS_XPLAN 패키지)

  • 실행계획만 수립
  • 데이터를 읽지 않음
  • I/O 관련 정보 확인 불가
  • 수행시간 추정 불가

 

DBMS_XPLAN 패키지 핵심 함수 상세 설명

1. DISPLAY(가장 기본)

  • plan_table에 저장된 내용을 출력하여 쿼리가 어떻게 작동할지 예측치를 볼 때 사용합니다.
  • 즉석 확인용으로 plan_table만 참조

👇 DBMS_XPLAN 패키지 - DISPLAY 기본 문법 형태

DBMS_XPLAN.DISPLAY(
    {TABLE_NAME},		-- 생략 가능(기본값 = 'PLAN_TABLE')
    {STATEMENT_ID},		-- 지정되지 않으면 '가장 최근의 명령문 실행계획 ID'
    {FORMAT},			-- format=[BASIC | TYPICAL | SERIAL | ALL]
    {FILTER_PREDS}		-- 행 세트를 제한하는 SQL필터 역할
)

 

2. DISPLAY_PLAN(리포트 및 관리용)

  • 다양한 형식으로 plan_table에 저장된 내용 표시하여 주로 이미 저장된 계획을 출력할 때 사용합니다.
    • format: BASIC, TYPICAL, SERIAL, ALL, ADPTIVE
    • type: TEXT, ACTIVE, HTML, XML

3. DISPLAY_CURSOR(실제 실행 정보)

  • 현재 메모리(Library Cache)에 올라와 있는 실제 실행된 쿼리의 계획을 보여줍니다.
  • DISPLAY는 예측치지만, 이건 실제 수행된 통계(실제 Row 수 등)를 보여줍니다.

👇 DBMS_XPLAN 패키지 - DISPLAY_CURSOR 기본 문법 형태

DBMS_XPLAN.DISPLAY_CURSOR(
    {SQL_ID},			-- 커서 캐시에 있는 SQL문의 SQL_ID를 지정
    {CURSOR_CHILD},		-- 표시할 커서의 자식번호
    {FORMAT}			-- format = [BASIC | TYPICAL | SERIAL | ALL | ADPTVIE]
)

 

4. DISPLAY_AWR(과거 기록 추적)

  •  AWR에 저장된 실행계획 표시하여 과거와 현재의 실행 계획을 비교하는 용도입니다.

5. DIFF_PLAN(계획 비교)

  • 두 개 이상의 실행 계획을 서로 비교하여 차이점을 하이라이트 해줍니다.
  • 인덱스 변경 전후나 파라미터 변경 전후의 계획 차이를 파악할 때 유용합니다.

6. DISPLAY_SQL_PLAN_BASELINE(안전장치 확인)

  • SQL Plan Baseline(SPB)의 지정된 SQL 핸들에 대해 하나 이상의 실행계획 표시합니다.
  • 오라클이 "이 쿼리는 무조건 이 계획으로만 돌아야 해" 라고 고정해둔 안전한 계획이 무엇인지 확인할 때 사용합니다.

7. DISPLAY_SQLSET

  • SQL Tuning Set 에 저장된 실행 계획을 표시합니다.
  • 대규모 마이그레이션이나 성능 테스트 시 수집된 쿼리 묶음을 분석할 때 사용합니다.

 

👇EXPLAIN PLAN 실습

더보기

👇'SQLP_TEST_02'라는 이름으로 예상 실행 계획 생성해서 PLAN_TABLE에 저장

SQL> EXPLAIN PLAN
	SET STATEMENT_ID = 'SQLP_TEST_02' INTO PLAN_TABLE
	FOR
	SELECT E.EMPNO,
		E.ENAME,
		(SELECT D.DNAME FROM DEPT D WHERE E.DEPTNO = D.DEPTNO) AS DNAME
	FROM EMP E
	WHERE 1=1
	ORDER BY DNAME, E.EMPNO;

Explained.
  • 'INTO PLAN_TABLE' 생략 가능(기본값)
  • 옵티마이저가 파싱된 결과를 가지고 어떻게 실행할지 실행계획을 세움

 

👇DBMS_XPLAN 패키지를 사용하여 PLAN_TABLE에서 'SQLP_TEST_02' 예상 실행 계획의 모든 정보 출력

SQL> select * from table(DBMS_XPLAN.DISPLAY('PLAN_TABLE','SQLP_TEST_02','ALL'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3952423604

---------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |  1000K|    18M|       |  9188   (1)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| DEPT        |     1 |    40 |       |     2   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | SYS_C007940 |     1 |       |       |     1   (0)| 00:00:01 |
|   3 |  SORT ORDER BY              |             |  1000K|    18M|    30M|  9188   (1)| 00:00:01 |
|   4 |   TABLE ACCESS FULL         | EMP         |  1000K|    18M|       |  3280   (1)| 00:00:01 |
---------------------------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$2 / D@SEL$2
   2 - SEL$2 / D@SEL$2
   3 - SEL$1
   4 - SEL$1 / E@SEL$1

Predicate Information (identified by operation id):
---------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

   2 - access("D"."DEPTNO"=:B1)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - "D"."DNAME"[VARCHAR2,50]
   2 - "D".ROWID[ROWID,10]
   3 - (#keys=2)  (SELECT "D"."DNAME" FROM "DEPT" "D" WHERE "D"."DEPTNO"=:B1)[50],
       "E"."EMPNO"[NUMBER,22], "E"."ENAME"[VARCHAR2,4000]
   4 - "E"."EMPNO"[NUMBER,22], "E"."ENAME"[VARCHAR2,4000], "E"."DEPTNO"[NUMBER,22]

33 rows selected.

 

 


 

2. AUTOTRACE

  • 쿼리문을 실행 시킬때 마다 실행계획을 바로 확인 가능
  • 통계정보, 실행계획, 실제 데이터 조회 여부 등을 조정 가능
  • AUTOTRACE를 사용하기 위해서는 'PLUSTRACE' 권한이 필요

👇 AUTOTRACE 기능 실습

더보기

👇AUTOTRACE 기능 활성화

SQL> SET AUTOTRACE ON;

 

👇 SELECT 수행

SQL> SELECT E.EMPNO
	, E.ENAME
	, (SELECT D.DNAME FROM DEPT D WHERE E.DEPTNO = D.DEPTNO) AS DNAME
FROM EMP E
WHERE 1=1
ORDER BY DNAME, E.EMPNO;

 

👇결과

			.
			.
			.
    999958 QRYHFYNJPI      Department_9
    999968 QFADPGVRBE      Department_9
    999978 ZMYKRTSBVI      Department_9
    999988 JXLXQNBXFD      Department_9

     EMPNO ENAME           DNAME
---------- --------------- ---------------
    999998 USEYTCKATB      Department_9

1000000 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 3952423604

---------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |  1000K|    18M|       |  9188   (1)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| DEPT        |     1 |    40 |       |     2   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | SYS_C007940 |     1 |       |       |     1   (0)| 00:00:01 |
|   3 |  SORT ORDER BY              |             |  1000K|    18M|    30M|  9188   (1)| 00:00:01 |
|   4 |   TABLE ACCESS FULL         | EMP         |  1000K|    18M|       |  3280   (1)| 00:00:01 |
---------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("D"."DEPTNO"=:B1)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
      11922  consistent gets
      11904  physical reads
          0  redo size
   33713887  bytes sent via SQL*Net to client
     735151  bytes received via SQL*Net from client
      66668  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
    1000000  rows processed

 


 

3. SQL TRACE

  • 실행 계획뿐만 아니라 여러 세션에서 수행한 SQL의 통계정보, 수행시간, 결과 등을 TRACE 파일(.trc)로 기록
  • 세션 레벨과 인스턴스 레벨에서 SQL 문장들을 분석 가능 (인스턴스 레벨은 DBMS 성능 저하 원인)

👇SQL TRACE 실습

더보기

👇 SQL TRACE 기능 활성화

SQL> ALTER SESSION SET SQL_TRACE = TRUE

 

👇현재 로그인한 사용자 세션에 대한 정보 조회(Trace 파일을 찾기 위함)

-- 현재 접속 중인 세션의 SID 조회
SQL> SELECT SYS_CONTEXT('USERENV', 'SID') FROM DUAL;

SYS_CONTEXT('USERENV','SID')
--------------------------------
36
-- SID로 현재 접속 중인 SPID 조회
SQL> SELECT B.SPID
        FROM V$SESSION A, V$PROCESS B
        WHERE A.SID=36
                AND B.ADDR = A.PADDR;

SPID
------------------------
1916

 

👇생성된 Trace파일 경로 조회

SQL> SHOW PARAMETER DIAG;

NAME                                 TYPE                 VALUE
------------------------------------ -------------------- ------------------------------
diagnostic_dest                      string               /u01/app/oracle
[oracle@orcl ~]$ cd /u01/app/oracle/diag/rdbms/orcl/orcl/trace
[oracle@orcl trace]$ ls -al *1916*
-rw-r-----. 1 oracle oinstall 159739 Mar 23 12:10 orcl_ora_1916.trc
-rw-r-----. 1 oracle oinstall  23516 Mar 23 12:10 orcl_ora_1916.trm

 

👇 orcl_ora_1916.trc 조회

Trace file /u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_1916.trc
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0
Build label:    RDBMS_19.3.0.0.0DBRU_LINUX.X64_190417
ORACLE_HOME:    /u01/app/oracle/product/19c/dbhome_1
System name:    Linux
Node name:      ora-act
Release:        5.15.0-200.131.27.el8uek.x86_64
Version:        #2 SMP Wed Oct 4 22:19:10 PDT 2023
Machine:        x86_64
Instance name: orcl
Redo thread mounted by this instance: 1
Oracle process number: 36
Unix process pid: 1916, image: oracle@ora-act


*** 2026-03-23T12:02:22.731360+09:00 (PDB(3))
*** SESSION ID:(36.61267) 2026-03-23T12:02:22.731409+09:00
*** CLIENT ID:() 2026-03-23T12:02:22.731417+09:00
*** SERVICE NAME:(pdb) 2026-03-23T12:02:22.731422+09:00
*** MODULE NAME:(SQL*Plus) 2026-03-23T12:02:22.731428+09:00
*** ACTION NAME:() 2026-03-23T12:02:22.731433+09:00
*** CLIENT DRIVER:(SQL*PLUS) 2026-03-23T12:02:22.731437+09:00
*** CONTAINER ID:(3) 2026-03-23T12:02:22.731442+09:00

CLOSE #140378401235840:c=11,e=11,dep=0,type=1,tim=8731502015
=====================
PARSING IN CURSOR #140378401078744 len=332 dep=1 uid=0 oct=3 lid=0 tim=8731506150 hv=2698389488 ad='785636d0' sqlid='acmvv4fhdc9zh'
select obj#,type#,ctime,mtime,stime, status, dataobj#, flags, oid$, spare1, spare2, spare3, signature, spare7, spare8, spare9, nvl(dflcollid, 16382), creappid, creverid, modappid, modverid, crepatchid, modpatchid from obj$ where owner#=:1 and name=:2 and namespace=:3 and remoteowner is null and linkname is null and subname is null
END OF STMT
EXEC #140378401078744:c=1673,e=2680,p=0,cr=0,cu=2,mis=1,r=0,dep=1,og=4,plh=813480514,tim=8731506146
FETCH #140378401078744:c=30,e=30,p=0,cr=3,cu=0,mis=0,r=0,dep=1,og=4,plh=813480514,tim=8731506265
STAT #140378401078744 id=1 cnt=0 pid=0 pos=1 obj=18 op='TABLE ACCESS BY INDEX ROWID BATCHED OBJ$ (cr=3 pr=0 pw=0 str=1 time=35 us cost=4 size=111 card=1)'
STAT #140378401078744 id=2 cnt=0 pid=1 pos=1 obj=37 op='INDEX RANGE SCAN I_OBJ2 (cr=3 pr=0 pw=0 str=1 time=33 us cost=3 size=0 card=1)'
CLOSE #140378401078744:c=1,e=1,dep=1,type=3,tim=8731506380
EXEC #140378401078744:c=20,e=20,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,plh=813480514,tim=8731506737
FETCH #140378401078744:c=28,e=28,p=0,cr=4,cu=0,mis=0,r=1,dep=1,og=4,plh=813480514,tim=8731506793
CLOSE #140378401078744:c=5,e=5,dep=1,type=3,tim=8731506819

*** 2026-03-23T12:02:22.737767+09:00 (CDB$ROOT(1))
EXEC #140378401078744:c=1569,e=2039,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,plh=813480514,tim=8731508930
FETCH #140378401078744:c=28,e=28,p=0,cr=4,cu=0,mis=0,r=1,dep=1,og=4,plh=813480514,tim=8731509069
CLOSE #140378401078744:c=9,e=8,dep=1,type=3,tim=8731509093
=====================

 


4. TKPROF - Trace File 분석

  • tkprof 명령은 trace 파일(.trc)을 text 파일 형태로 읽기 쉽게 변환

👇tkprof 실습

더보기

👇tkprof 사용하여 txt로 변환하기

[oracle@orcl trace]$ tkprof /u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_1916.trc /home/oracle/orcl_ora_1916.trc

 

👇조회 결과

[oracle@orcl ~]$ vi /home/oracle/orcl_ora_1916.trc

TKPROF: Release 19.0.0.0.0 - Development on Mon Mar 23 12:24:48 2026

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.

Trace file: /u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_1916.trc
Sort options: default

********************************************************************************
count    = number of times OCI procedure was executed
cpu      = cpu time in seconds executing
elapsed  = elapsed time in seconds executing
disk     = number of physical reads of buffers from disk
query    = number of buffers gotten for consistent read
current  = number of buffers gotten in current mode (usually for update)
rows     = number of rows processed by the fetch or execute call
********************************************************************************

SQL ID: acmvv4fhdc9zh Plan Hash: 813480514

select obj#,type#,ctime,mtime,stime, status, dataobj#, flags, oid$, spare1,
  spare2, spare3, signature, spare7, spare8, spare9, nvl(dflcollid, 16382),
  creappid, creverid, modappid, modverid, crepatchid, modpatchid
from
 obj$ where owner#=:1 and name=:2 and namespace=:3 and remoteowner is null
  and linkname is null and subname is null


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        0      0.00       0.00          0          0          0           0
Execute     14      0.00       0.00          0          0          2           0
Fetch       14      0.00       0.00          1         53          0          11
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       28      0.00       0.00          1         53          2          11

Misses in library cache during parse: 0
Misses in library cache during execute: 2
Optimizer mode: CHOOSE
Parsing user id: SYS   (recursive depth: 1)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         0          0          0  TABLE ACCESS BY INDEX ROWID BATCHED OBJ$ (cr=3 pr=0 pw=0 time=35 us starts=1 cost=4 size=111 card=1)
         0          0          0   INDEX RANGE SCAN I_OBJ2 (cr=3 pr=0 pw=0 time=33 us starts=1 cost=3 size=0 card=1)(object id 37)
.
.
.

 

 


 

⭐SQL 응답 시간

응답 시간(Respones Time)
    = Service Time + Wait Time
    = CPU Time + Queue Time

 

각종 시간/정보를 확인하기 위한 뷰

  • V$SQL
  • V$SQLTEXT
  • AWR
    • 기본 1시간마다 DB에 대한 통계 및 성능자료 등을 수집해 스냅샷으로 만들어 일정기간(8) 보관하여 활용할 수 있는 기능
    • Buffer / CPU / Pin / Latch / Library 등의 Hit Ratio(적중률), 자원 사용률, Sort/Hard Pares 정도, SQL Statistics(느린 쿼리) 등 주요 성능지표를 제공
    • AWR 리포트 하단에 Adbisory(권고) 섹션을 통해 효율성을 높일 수 있습니다. ex) "만약 버퍼 캐시를 2GB 늘린다면 물리적 I/O가 30% 줄어들 것입니다"라는 식의 시뮬레이션 결과를 보여줍니다.

👇AWR 성능 지표 정보

더보기
  • Workload Repository report for (일반 정보)
    • DB name, DB ID, Instance, Inst Num, Startup time, Release, RAC, Host Name, Platform, CPUs, Cores, Sockets, Memory, Snap ID, Snap Time, Sessions, Curs/Sess, Elasped, DB times,
    • Cache Sizes
      • Buffer Cache, Shared Pool Size, Std block Size, Log Buffer
    • Load Profile
      • DB Time, DB CPU, Logical reads, Block changes, Physical reads, Physical writes, User calls, Parses, Hard Parses, W/A MB Processed, Logons, Executes, Rollbacks, Transactions
    • Instance Efficiency Percentages
      • Buffer Nowait%, Buffer Hit%, Library Hit%, Execute to Parse%, Parse CPU to Parse Elapsed%, Redo NoWait%, In_memory Sort%, Soft Parse%, %Non-Parse CPU
      • Shared Pool Statistics: Memory Usage%, %SQL with executions>1, %Memory for SQL 2/exec>1
    • Top 5 Wait statistics
    • Host CPU
    • Instance CPU
    • Memory Statistics: Host Mem, SGA use, PGA use, %Host mem used for SGA+PGA

 


 

💡시나리오로 감 잡기

1. "개발 서버에선 빨랐는데, 운영 서버에선 왜 느릴까?"

  • 상황: 개발자가 EXPLAIN PLAN으로 실행 계획을 확인했을 때는 인덱스를 잘 타는 것으로 나왔습니다. 그런데 실제 운영 환경에 데이터를 넣고 돌려보니 쿼리가 원할하지 않습니다.
  • [잠시 생각해보세요!] 🧐 '분명히 인덱스 스캔(INDEX RANGE SCAN)이 뜬다고 확인했는데, 왜 실제로는 느린 걸까요?'
  •  
  • 👇 원인과 해결
더보기
  • 원인: EXPLAIN PLAN은 예측치일 뿐입니다. 실제 데이터의 분포도나 메모리 상태를 반영하지 못할 때가 많죠. 특히 바인드 변수를 쓸 때 옵티마이저가 실제 값을 모르고 세운 계획일 수 있습니다.
  • 해결: DBMS_XPLAN.DISPLAY_CURSOR를 사용하세요. 이는 실제 메모리(Library Cache)에 올라온 진짜 실행 계획과 실제 처리된 Row 수를 보여줍니다. 예측과 실제의 차이를 발견하는 첫걸음입니다.

 

 

2. "특정 시간대에 응답이 느린데, 조사해 볼까?"

  • 상황: 특정 업무 시간에만 유독 DB 응답이 늦어지는데, 대상을 특정하기 어렵습니다. 전체를 보기엔 부하가 너무 커서 특정 세션만 집중 조사하기로 했습니다.
  • [잠시 생각해보세요!] 🧐 '단순히 쿼리문만 본다고 해서 어디서 시간이 많이 걸리는지 알 수 있을까요? CPU를 많이 쓰는지, 디스크 I/O를 많이 하는지 어떻게 구분하죠?'

👇원인과 해결

더보기
  • 원인: 실행 계획만으로는 **대기 이벤트(Wait Event)**나 구체적인 **Call 통계(Parse, Execute, Fetch)**를 알 수 없기 때문입니다.
  • 해결: ALTER SESSION SET SQL_TRACE = TRUE로 해당 세션의 발자취를 남긴 뒤, TKPROF로 분석하세요. cpu 시간과 elapsed 시간의 차이를 보면 쿼리가 CPU를 태우고 있는지, 아니면 무언가를 기다리고 있는지(Wait Time) 명확히 드러납니다. 만약 특정 세션만 조사 하는게 아니라면 AWR을 사용하여 조사하여도 좋습니다.

 

3. "어제 오후 2시에 발생한 장애, 지금 확인할 수 있을까?"

  • 상황: 어제 갑자기 DB가 느려졌다는 보고를 오늘 아침에 받았습니다. 현재는 정상이라 실시간 모니터링으로는 원인을 알 수 없습니다.
  • [잠시 생각해보세요!] 🧐 '이미 지나간 과거의 성능 데이터를 어디서 찾아야 할까요? 

👇원인과 해결

더보기
  • 원인: 오라클의 AWR(Automatic Workload Repository)이 1시간마다 스냅샷을 찍어 과거의 성능 지표를 저장하고 있기 때문입니다.
  • 해결: 장애 시간대의 AWR 리포트를 뽑으세요. 리포트의 SQL Statistics 섹션에서 그 시간대 가장 많은 자원을 먹은 'Top SQL'을 찾고, 하단의 Advisory 섹션을 통해 메모리(Buffer Cache, Shared Pool) 증설이 필요한지 시뮬레이션 결과를 확인합니다.

 

4.  "아무것도 안 건드렸는데 갑자기 쿼리가 느려졌어요!"

  • 상황: 몇 달 동안 잘 돌아가던 배치 쿼리가 오늘 갑자기 평소보다 10배나 오래 걸립니다. 소스 코드도, 인덱스도 바뀐 게 없습니다.
  • [잠시 생각해보세요!] 🧐 '쿼리도 그대로고 데이터 양도 비슷한데, 왜 갑자기 실행 계획이 풀 스캔(Full Scan)으로 바뀌었을까요?'

👇원인과 해결

더보기
  • 원인: 주기적인 통계 정보 수집(DBMS_STATS) 결과 데이터 분포도가 미세하게 바뀌면서 옵티마이저가 '변심'을 한 것입니다.
  • 해결: DBMS_XPLAN.DIFF_PLAN으로 어제의 계획과 오늘의 계획을 비교해 보세요. 변심한 것이 확인되었다면, SQL Plan Baseline(SPB) 기능을 사용해 예전의 좋았던 계획으로 고정(Fix)시켜 안정성을 되찾아야 합니다.