메뉴 여닫기
개인 메뉴 토글
로그인하지 않음
만약 지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.
Oracle (토론 | 기여)님의 2025년 6월 16일 (월) 22:29 판 (새 문서: Oracle 19c에서 성능이 좋지 않은 SQL을 AWR에서 추출하고, 그 이력을 관리하는 테이블에 저장한 후 SQL로 조회할 수 있는 간단한 프로그램을 아래와 같이 구성해드릴게요. ⸻ ✅ 1. 개요 • 대상: AWR에서 수집된 SQL 중 Elapsed Time 또는 Buffer Gets가 높은 SQL • 저장처: 사용자 정의 이력 테이블 • 주기: 수동 실행 혹은 스케줄러로 주기적 실행 가능 • 기능: • AWR에서 TOP...)
(차이) ← 이전 판 | 최신판 (차이) | 다음 판 → (차이)

Oracle 19c에서 성능이 좋지 않은 SQL을 AWR에서 추출하고, 그 이력을 관리하는 테이블에 저장한 후 SQL로 조회할 수 있는 간단한 프로그램을 아래와 같이 구성해드릴게요.

✅ 1. 개요 • 대상: AWR에서 수집된 SQL 중 Elapsed Time 또는 Buffer Gets가 높은 SQL • 저장처: 사용자 정의 이력 테이블 • 주기: 수동 실행 혹은 스케줄러로 주기적 실행 가능 • 기능: • AWR에서 TOP SQL 추출 • 사용자 테이블에 INSERT (중복 방지) • 조회용 SELECT 제공

✅ 2. AWR에서 TOP SQL 추출 쿼리 (Elapsed Time 기준)

SELECT

   s.snap_id,
   TO_CHAR(s.begin_interval_time, 'YYYY-MM-DD HH24:MI') AS begin_time,
   TO_CHAR(s.end_interval_time, 'YYYY-MM-DD HH24:MI') AS end_time,
   sq.sql_id,
   sq.plan_hash_value,
   sq.module,
   sq.parsing_schema_name,
   sq.executions_delta,
   sq.elapsed_time_delta,
   ROUND(sq.elapsed_time_delta / GREATEST(sq.executions_delta, 1)) AS avg_elapsed,
   sq.buffer_gets_delta,
   sq.disk_reads_delta,
   sq.rows_processed_delta

FROM

   dba_hist_snapshot s

JOIN

   dba_hist_sqlstat sq ON sq.snap_id = s.snap_id AND sq.dbid = s.dbid AND sq.instance_number = s.instance_number

WHERE

   s.begin_interval_time >= SYSDATE - 1

ORDER BY

   sq.elapsed_time_delta DESC

FETCH FIRST 50 ROWS ONLY;


✅ 3. 저장 테이블 생성

CREATE TABLE perf_sql_history (

   id                 NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
   snap_id            NUMBER,
   begin_time         DATE,
   end_time           DATE,
   sql_id             VARCHAR2(20),
   plan_hash_value    NUMBER,
   module             VARCHAR2(64),
   parsing_schema     VARCHAR2(30),
   executions         NUMBER,
   elapsed_time       NUMBER,
   avg_elapsed        NUMBER,
   buffer_gets        NUMBER,
   disk_reads         NUMBER,
   rows_processed     NUMBER,
   inserted_at        DATE DEFAULT SYSDATE

);


✅ 4. 중복 방지 및 INSERT 프로시저

CREATE OR REPLACE PROCEDURE collect_top_sql_from_awr IS BEGIN

   INSERT INTO perf_sql_history (
       snap_id, begin_time, end_time, sql_id, plan_hash_value,
       module, parsing_schema, executions, elapsed_time, avg_elapsed,
       buffer_gets, disk_reads, rows_processed
   )
   SELECT 
       s.snap_id,
       s.begin_interval_time,
       s.end_interval_time,
       sq.sql_id,
       sq.plan_hash_value,
       sq.module,
       sq.parsing_schema_name,
       sq.executions_delta,
       sq.elapsed_time_delta,
       ROUND(sq.elapsed_time_delta / GREATEST(sq.executions_delta, 1)),
       sq.buffer_gets_delta,
       sq.disk_reads_delta,
       sq.rows_processed_delta
   FROM
       dba_hist_snapshot s
   JOIN
       dba_hist_sqlstat sq ON sq.snap_id = s.snap_id AND sq.dbid = s.dbid AND sq.instance_number = s.instance_number
   WHERE
       s.begin_interval_time >= SYSDATE - 1
       AND NOT EXISTS (
           SELECT 1 FROM perf_sql_history h
           WHERE h.snap_id = s.snap_id AND h.sql_id = sq.sql_id
       )
   ORDER BY
       sq.elapsed_time_delta DESC
   FETCH FIRST 50 ROWS ONLY;
   
   COMMIT;

END; /


✅ 5. 조회 SQL 예제

최근 일주일간 가장 느렸던 SQL 조회:

SELECT * FROM perf_sql_history WHERE inserted_at >= SYSDATE - 7 ORDER BY elapsed_time DESC;


✅ 6. 자동 실행 (옵션: DBMS_SCHEDULER)

BEGIN

 DBMS_SCHEDULER.create_job (
   job_name        => 'JOB_COLLECT_TOP_SQL',
   job_type        => 'PLSQL_BLOCK',
   job_action      => 'BEGIN collect_top_sql_from_awr; END;',
   start_date      => SYSTIMESTAMP,
   repeat_interval => 'FREQ=HOURLY;INTERVAL=2',  -- 2시간마다 실행
   enabled         => TRUE,
   comments        => 'Collect top SQL from AWR every 2 hours');

END; /


✅ 7. 확장 아이디어 • SQL Text까지 저장하고 싶다면 dba_hist_sqltext 조인 • PLAN 상세 추적: dba_hist_sql_plan 이용 • 특정 모듈/사용자 필터링


좋습니다! 요청하신 다음 3가지 기능을 추가하여 AWR 성능저하 SQL 추적 시스템을 확장하겠습니다:

✅ 1. dba_hist_sqltext 조인하여 SQL Text 저장

✅ 2. dba_hist_sql_plan 이용하여 PLAN 상세 추적

✅ 3. Bind 변수 값 저장 (via v$sql_bind_capture)

✅ 0. 저장 테이블 구조 확장

CREATE TABLE perf_sql_history (

   id                 NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
   snap_id            NUMBER,
   begin_time         DATE,
   end_time           DATE,
   sql_id             VARCHAR2(20),
   plan_hash_value    NUMBER,
   module             VARCHAR2(64),
   parsing_schema     VARCHAR2(30),
   executions         NUMBER,
   elapsed_time       NUMBER,
   avg_elapsed        NUMBER,
   buffer_gets        NUMBER,
   disk_reads         NUMBER,
   rows_processed     NUMBER,
   sql_text           CLOB,
   inserted_at        DATE DEFAULT SYSDATE

);

✅ sql_text 컬럼을 CLOB으로 지정 🛠️ 테이블 생성 후 CLOB 저장을 위해 LOB 저장 방식 (SecureFiles) 설정 확인 필요할 수 있음

✅ 1. SQL Text 저장 (from dba_hist_sqltext)

AWR에서는 dba_hist_sqltext 에 SQL Text가 저장되어 있으며, sql_id 기준으로 조인합니다.

▶️ 프로시저 수정

CREATE OR REPLACE PROCEDURE collect_top_sql_from_awr IS BEGIN

   INSERT INTO perf_sql_history (
       snap_id, begin_time, end_time, sql_id, plan_hash_value,
       module, parsing_schema, executions, elapsed_time, avg_elapsed,
       buffer_gets, disk_reads, rows_processed, sql_text
   )
   SELECT 
       s.snap_id,
       s.begin_interval_time,
       s.end_interval_time,
       sq.sql_id,
       sq.plan_hash_value,
       sq.module,
       sq.parsing_schema_name,
       sq.executions_delta,
       sq.elapsed_time_delta,
       ROUND(sq.elapsed_time_delta / GREATEST(sq.executions_delta, 1)),
       sq.buffer_gets_delta,
       sq.disk_reads_delta,
       sq.rows_processed_delta,
       st.sql_text
   FROM
       dba_hist_snapshot s
   JOIN
       dba_hist_sqlstat sq ON sq.snap_id = s.snap_id AND sq.dbid = s.dbid AND sq.instance_number = s.instance_number
   JOIN
       dba_hist_sqltext st ON st.sql_id = sq.sql_id
   WHERE
       s.begin_interval_time >= SYSDATE - 1
       AND NOT EXISTS (
           SELECT 1 FROM perf_sql_history h
           WHERE h.snap_id = s.snap_id AND h.sql_id = sq.sql_id
       )
   ORDER BY
       sq.elapsed_time_delta DESC
   FETCH FIRST 50 ROWS ONLY;
   COMMIT;

END; /


✅ 2. PLAN 상세 추적 저장용 테이블

▶️ PLAN 저장 테이블

CREATE TABLE perf_sql_plan (

   id              NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
   history_id      NUMBER, -- perf_sql_history.id 참조
   operation_id    NUMBER,
   operation       VARCHAR2(50),
   options         VARCHAR2(50),
   object_owner    VARCHAR2(30),
   object_name     VARCHAR2(128),
   object_type     VARCHAR2(30),
   access_predicates  VARCHAR2(4000),
   filter_predicates  VARCHAR2(4000),
   cardinality     NUMBER,
   cost            NUMBER,
   depth           NUMBER

);

▶️ PLAN 저장 프로시저 (간단 예시)

CREATE OR REPLACE PROCEDURE collect_sql_plan(p_sql_id VARCHAR2, p_plan_hash NUMBER, p_history_id NUMBER) IS BEGIN

   INSERT INTO perf_sql_plan (
       history_id, operation_id, operation, options,
       object_owner, object_name, object_type,
       access_predicates, filter_predicates, cardinality, cost, depth
   )
   SELECT
       p_history_id,
       id,
       operation,
       options,
       object_owner,
       object_name,
       object_type,
       access_predicates,
       filter_predicates,
       cardinality,
       cost,
       depth
   FROM
       dba_hist_sql_plan
   WHERE
       sql_id = p_sql_id
       AND plan_hash_value = p_plan_hash;
   COMMIT;

END; /

▶️ 메인 프로시저에서 호출

perf_sql_history 저장 후 해당 id를 이용해 collect_sql_plan 호출하는 방식으로 확장 가능합니다. (PL/SQL 내에서 RETURNING id INTO 구문 활용)

✅ 3. Bind 변수 저장

AWR은 bind 값을 저장하지 않지만, GV$SQL_BIND_CAPTURE 를 통해 현재 캐시된 바인드 값을 조회할 수 있습니다.

▶️ 바인드 값 테이블

CREATE TABLE perf_sql_bind (

   id            NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
   sql_id        VARCHAR2(20),
   name          VARCHAR2(30),
   position      NUMBER,
   datatype      VARCHAR2(30),
   value_string  VARCHAR2(4000),
   captured_at   DATE DEFAULT SYSDATE

);

▶️ 바인드 저장 프로시저

CREATE OR REPLACE PROCEDURE collect_bind_values(p_sql_id VARCHAR2) IS BEGIN

   INSERT INTO perf_sql_bind (sql_id, name, position, datatype, value_string)
   SELECT
       sql_id,
       name,
       position,
       datatype_string,
       value_string
   FROM
       gv$sql_bind_capture
   WHERE
       sql_id = p_sql_id;
   COMMIT;

END; /

⚠️ 해당 값은 현재 SGA에 남아있는 바인드 값에 한정됩니다. AWR에는 바인드 값이 저장되지 않기 때문에 이 부분은 실시간 수집 시 병행 해야 합니다.

✅ 향후 확장 아이디어

기능 설명 SQL Text 길이 초과 방지 4000자 이상인 SQL은 별도 처리 필요 자동 수집 스케줄링 DBMS_SCHEDULER 주기적 실행 가능 대시보드 APEX, Python Flask 등으로 Web View 구성 가능 SQL Text 중 중복 제거 Hash나 SQL_ID 기반 중복 방지