모니터링과 성능 진단
운영 중인 데이터베이스는 지속적으로 관찰해야 합니다. 모니터링의 목적은 문제가 발생한 뒤 숫자를 보는 것이 아니라, 평상시 기준선(Baseline)을 알고 있다가 변화가 생겼을 때 원인을 빠르게 좁히는 것입니다.
좋은 진단은 수치 → 대기 원인 → 문제 SQL → 실행 계획 → 조치 후 검증의 순서로 진행됩니다. CPU나 캐시 적중률 같은 단일 지표만으로 결론을 내리면 오진하기 쉽습니다.
핵심 메트릭
모니터링 지표는 “높다/낮다”보다 평소보다 어떻게 달라졌는지가 중요합니다. 같은 CPU 80%도 배치 시간에는 정상일 수 있고, 평소 20%이던 시스템에서는 장애 신호일 수 있습니다.
| 메트릭 | 설명 | 진단 관점 |
|---|---|---|
| TPS / QPS | 초당 트랜잭션·쿼리 처리량 | 급락하면 병목, 장애, 잠금 대기 가능성 확인 |
| DB Time / Query Latency | DB 안에서 소비된 시간과 응답 지연 | 사용자 지연과 가장 직접적으로 연결 |
| Active Sessions | CPU 사용 또는 비 idle 대기 중인 세션 | CPU 포화인지, 락/IO 대기인지 wait class로 분해 |
| Slow Query | 기준 시간보다 긴 쿼리 | 개별 장기 쿼리와 짧지만 빈번한 쿼리를 구분 |
| Lock Wait | 잠금 때문에 대기하는 세션 | 블로커, 트랜잭션 길이, 접근 순서 확인 |
| Logical / Physical I/O | 버퍼 읽기와 실제 디스크 읽기 | 캐시 문제인지, SQL 접근 경로 문제인지 구분 |
| Storage / Tablespace | 저장 공간과 증가 속도 | 절대 사용률보다 증가 추세와 자동 확장 여유 확인 |
| Connection Pool | active, idle, pending, timeout | DB 병목인지 애플리케이션 점유 문제인지 분리 |
| Replication Lag | 복제 지연 시간 | 읽기 정합성, 장애 전환, 백업 부하 영향 확인 |
Oracle 성능 진단 도구
Oracle은 AWR, ASH, ADDM, V$ 동적 성능 뷰를 통해 워크로드와 대기 이벤트를 분석할 수 있습니다. 다만 AWR/ASH/ADDM과 관련 리포트·뷰는 환경에 따라 Oracle Diagnostics Pack 라이선스 대상이므로 운영에서 사용하기 전에 라이선스 범위를 확인해야 합니다.
AWR (Automatic Workload Repository)
AWR은 Oracle이 수집하는 성능 통계 스냅샷입니다. 기본적으로 1시간 간격으로 스냅샷을 만들고 8일 동안 보관합니다. 장애 분석에서는 단일 스냅샷보다 정상 구간과 문제 구간의 차이를 비교하는 것이 핵심입니다.
-- AWR 스냅샷 목록 확인
SELECT snap_id, begin_interval_time, end_interval_time
FROM dba_hist_snapshot
ORDER BY snap_id DESC
FETCH FIRST 10 ROWS ONLY;
-- AWR 리포트 생성 (HTML)
@$ORACLE_HOME/rdbms/admin/awrrpt.sql
-- 수동 스냅샷 생성 (장애 전후 비교용)
EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT();
-- 보관 주기 변경 (30일, 30분 간격)
EXEC DBMS_WORKLOAD_REPOSITORY.MODIFY_SNAPSHOT_SETTINGS(
retention => 43200, -- 30일 (분 단위)
interval => 30 -- 30분 간격
);| AWR 섹션 | 확인 내용 | 관점 |
|---|---|---|
| Load Profile | DB Time, 실행 수, 논리/물리 읽기 | 전체 부하 변화 |
| Top Timed Events | 가장 많은 시간을 소비한 대기 이벤트 | CPU, I/O, 락 병목 분리 |
| SQL ordered by Elapsed Time | 총 소요 시간이 큰 SQL | 사용자 지연 후보 |
| SQL ordered by CPU Time | CPU를 많이 쓴 SQL | 연산량·함수·조인 확인 |
| SQL ordered by Gets | 논리 읽기가 많은 SQL | 인덱스·접근 경로 확인 |
| SQL ordered by Executions | 실행 횟수가 많은 SQL | 캐싱, 배치, N+1 의심 |
| Instance Activity Stats | 인스턴스 전반 통계 | 평소 기준선과 비교 |
| Segment Statistics | 특정 테이블/인덱스의 I/O와 경합 | 객체 단위 병목 후보 |
ASH (Active Session History)
ASH는 활성 세션을 샘플링한 데이터입니다. Oracle은 CPU를 쓰거나 idle이 아닌 대기 이벤트에 걸린 세션을 활성 세션으로 보고, V$ACTIVE_SESSION_HISTORY에 1초 단위 샘플을 노출합니다. AWR이 구간 보고서라면 ASH는 특정 시점의 “무엇을 기다렸는가”를 좁히는 도구에 가깝습니다.
-- 최근 5분간 가장 많이 대기한 이벤트
SELECT event, COUNT(*) AS samples,
ROUND(COUNT(*) * 100 / SUM(COUNT(*)) OVER (), 1) AS pct
FROM v$active_session_history
WHERE sample_time > SYSDATE - 5/1440
GROUP BY event
ORDER BY COUNT(*) DESC;
-- 특정 SQL이 무엇을 대기하는지
SELECT event, wait_class, COUNT(*) AS samples
FROM v$active_session_history
WHERE sql_id = 'abc123def456'
AND sample_time > SYSDATE - 1/24
GROUP BY event, wait_class
ORDER BY samples DESC;
-- 블로킹 세션 후보
SELECT blocking_session, COUNT(*) AS samples
FROM v$active_session_history
WHERE blocking_session IS NOT NULL
AND sample_time > SYSDATE - 10/1440
GROUP BY blocking_session
ORDER BY samples DESC;ADDM (Automatic Database Diagnostic Monitor)
ADDM은 AWR 데이터를 바탕으로 문제 원인과 권고안을 생성합니다. 권고안은 출발점으로 보고, 실제 변경은 SQL, 실행 계획, 업무 시간대, 애플리케이션 배포 이력을 함께 확인한 뒤 적용해야 합니다.
-- ADDM 리포트 자동 확인 (최근 스냅샷)
SELECT dbms_advisor.get_task_report(task_name) AS report
FROM dba_advisor_tasks
WHERE task_name LIKE 'ADDM%'
ORDER BY created DESC
FETCH FIRST 1 ROW ONLY;
-- ADDM 권고안 확인
SELECT f.finding_name, r.type, r.message
FROM dba_advisor_findings f
JOIN dba_advisor_recommendations r
ON f.task_id = r.task_id AND f.finding_id = r.finding_id
WHERE f.task_id = (SELECT MAX(task_id) FROM dba_advisor_tasks
WHERE advisor_name = 'ADDM');V$ 동적 성능 뷰
-- 현재 활성 세션과 실행 중인 SQL
SELECT s.sid, s.serial#, s.username, s.status,
s.sql_id, q.sql_text,
s.event, s.wait_class,
s.seconds_in_wait
FROM v$session s
LEFT JOIN v$sql q ON s.sql_id = q.sql_id AND s.sql_child_number = q.child_number
WHERE s.status = 'ACTIVE' AND s.username IS NOT NULL;
-- 락 대기 관계 (블로커 → 웨이터)
SELECT
blocker.sid AS blocker_sid,
blocker.username AS blocker_user,
waiter.sid AS waiter_sid,
waiter.username AS waiter_user,
waiter.event AS wait_event,
waiter.seconds_in_wait AS wait_seconds
FROM v$session waiter
JOIN v$session blocker ON waiter.blocking_session = blocker.sid
WHERE waiter.blocking_session IS NOT NULL;
-- Buffer Cache Hit Ratio (단독 결론 금지, 보조 지표로만 사용)
SELECT ROUND(
(1 - (phy.value / NULLIF(cur.value + con.value, 0))) * 100, 2
) AS hit_ratio
FROM v$sysstat phy, v$sysstat cur, v$sysstat con
WHERE phy.name = 'physical reads'
AND cur.name = 'db block gets'
AND con.name = 'consistent gets';
-- SGA 메모리 구성 확인
SELECT component, current_size/1024/1024 AS size_mb
FROM v$sga_dynamic_components
WHERE current_size > 0
ORDER BY current_size DESC;
-- Tablespace 사용률
SELECT tablespace_name,
ROUND(used_space * 8192 / 1024 / 1024) AS used_mb,
ROUND(tablespace_size * 8192 / 1024 / 1024) AS total_mb,
ROUND(used_percent, 1) AS used_pct
FROM dba_tablespace_usage_metrics
ORDER BY used_percent DESC;MySQL 성능 진단 도구
Performance Schema
Performance Schema는 MySQL 서버 내부의 실행, 대기, I/O, 락 정보를 관찰하는 프레임워크입니다. events_statements_summary_by_digest는 비슷한 SQL을 정규화해 묶어 주므로, “한 번 아주 느린 쿼리”와 “짧지만 매우 자주 실행되는 쿼리”를 함께 찾는 데 유용합니다.
-- Slow Query 상위 10개 (정규화된 digest 기준)
SELECT DIGEST_TEXT, COUNT_STAR,
ROUND(SUM_TIMER_WAIT/1000000000000, 3) AS total_sec,
ROUND(AVG_TIMER_WAIT/1000000000000, 3) AS avg_sec,
SUM_ROWS_EXAMINED, SUM_ROWS_SENT
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;
-- 현재 실행 중인 쿼리
SELECT thread_id, event_name, sql_text,
ROUND(timer_wait/1000000000000, 3) AS elapsed_sec
FROM performance_schema.events_statements_current
WHERE sql_text IS NOT NULL;
-- 테이블별 I/O 통계
SELECT object_schema, object_name,
count_read, count_write,
ROUND(sum_timer_read/1000000000000, 3) AS read_sec,
ROUND(sum_timer_write/1000000000000, 3) AS write_sec
FROM performance_schema.table_io_waits_summary_by_table
WHERE object_schema NOT IN ('mysql', 'performance_schema')
ORDER BY sum_timer_wait DESC
LIMIT 10;
-- MySQL 8.0.1+ InnoDB 락 대기
SELECT waiting_engine_transaction_id AS waiting_trx,
blocking_engine_transaction_id AS blocking_trx
FROM performance_schema.data_lock_waits;Slow Query Log
Slow Query Log는 지정한 시간보다 오래 걸린 쿼리를 파일 또는 테이블에 남깁니다. long_query_time은 워크로드 기준선에 맞춰 정하고, log_queries_not_using_indexes는 로그가 급증할 수 있으므로 짧은 진단 구간이나 throttle과 함께 쓰는 편이 안전합니다.
-- 현재 설정 확인
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';
-- 동적으로 Slow Query Log 활성화
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- 기준선에 맞게 조정
SET GLOBAL log_queries_not_using_indexes = 'ON';
-- 미인덱스 쿼리 로그가 폭증할 때는 throttle 검토
SET GLOBAL log_throttle_queries_not_using_indexes = 60;
-- InnoDB 상태 확인
SHOW ENGINE INNODB STATUS;
-- → TRANSACTIONS, SEMAPHORES, BUFFER POOL 섹션 확인PostgreSQL 성능 진단 도구
PostgreSQL은 누적 통계 뷰와 확장 기능을 조합해 진단합니다. pg_stat_statements는 SQL별 planning/execution 통계를 모으는 핵심 확장이지만, shared_preload_libraries에 등록하고 서버를 재시작해야 활성화됩니다.
-- postgresql.conf
-- shared_preload_libraries = 'pg_stat_statements'
-- 데이터베이스별로 확장 생성
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-- 가장 시간이 많이 걸리는 쿼리 상위 10개
SELECT query,
calls,
ROUND(total_exec_time::numeric, 2) AS total_ms,
ROUND(mean_exec_time::numeric, 2) AS avg_ms,
rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
-- 현재 실행 중인 세션
SELECT pid, usename, state, wait_event_type, wait_event,
now() - query_start AS running_time,
query
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY running_time DESC;
-- 테이블 통계 (순차 스캔 vs 인덱스 스캔)
SELECT relname,
seq_scan, idx_scan,
CASE WHEN seq_scan + idx_scan > 0
THEN ROUND(idx_scan * 100.0 / (seq_scan + idx_scan), 1)
ELSE 0 END AS idx_scan_pct,
n_tup_ins, n_tup_upd, n_tup_del,
n_dead_tup
FROM pg_stat_user_tables
ORDER BY seq_scan DESC;
-- 캐시 적중률 (보조 지표)
SELECT
sum(heap_blks_hit) * 100.0 /
NULLIF(sum(heap_blks_hit) + sum(heap_blks_read), 0) AS cache_hit_ratio
FROM pg_statio_user_tables;
-- VACUUM 필요성 확인
SELECT relname,
n_live_tup, n_dead_tup,
ROUND(n_dead_tup * 100.0 / NULLIF(n_live_tup, 0), 1) AS dead_pct,
last_autovacuum
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;
-- 실행 계획 확인: DML은 트랜잭션 안에서 롤백하며 확인
BEGIN;
EXPLAIN (ANALYZE, BUFFERS) SELECT ...;
ROLLBACK;외부 모니터링 도구
| 도구 | 특징 | 적합한 환경 |
|---|---|---|
| Oracle Enterprise Manager (OEM) | Oracle 공식 GUI, 종합 관리 | Oracle 전용 |
| Grafana + Prometheus | 오픈소스, 대시보드 커스터마이징 | 범용 |
| Datadog | SaaS, 다양한 DB 지원, APM 통합 | 클라우드·혼합 환경 |
| Zabbix | 오픈소스, 알림 설정, 에이전트 기반 | 온프레미스 |
| PMM (Percona Monitoring) | MySQL/PostgreSQL 전문 대시보드 | MySQL/PostgreSQL |
| CloudWatch | RDS/Aurora 지표와 로그 연계 | AWS |
모니터링 체계는 보통 수집 → 저장 → 시각화 → 알림 → 대응 기록으로 구성합니다. 대시보드는 한 화면에 모든 지표를 넣기보다, 증상별로 “사용자 지연”, “DB 내부 대기”, “문제 SQL”, “리소스”를 빠르게 이동할 수 있게 나누는 편이 좋습니다.
알림 설정 기준
알림 기준은 고정 숫자보다 기준선과 변화율을 함께 써야 합니다. 아래 값은 예시 기준이며, 실제 운영에서는 업무 시간대, 배치 시간, 스케일 정책, 복제 구조에 맞게 조정합니다.
| 항목 | 경고 예시 | 위험 예시 | 조치 |
|---|---|---|---|
| CPU 사용률 | 평소보다 30% 이상 상승 | 포화 상태가 지속 | SQL·배치·스케줄러·스케일 정책 확인 |
| 메모리 사용률 | 여유 메모리 급감 | swap 또는 OOM 위험 | 버퍼/작업 메모리, 누수, 연결 수 확인 |
| 디스크 사용률 | 증가 속도 급증 | 자동 확장 불가 또는 90% 근접 | 공간 확보, 보관 정책, 파티셔닝 검토 |
| Active Sessions | 평소 상위 분위수 초과 | CPU 수 대비 과도하거나 대기 급증 | wait class, 락, SQL별 DB Time 확인 |
| Slow Query | 기준선 대비 건수·시간 증가 | 사용자 지연과 timeout 동반 | digest/plan/배포 변경점 확인 |
| Lock Wait | 블로커가 반복 출현 | 장기 트랜잭션이 서비스 지연 유발 | 트랜잭션 범위 축소, 접근 순서 점검 |
| Cache Hit Ratio | 평소 대비 급락 | 물리 읽기와 latency 동시 증가 | SQL 접근 경로, 버퍼, working set 확인 |
| Replication Lag | 읽기 지연 기준 초과 | 장애 전환·정합성 위험 | 복제 부하, 네트워크, 장기 트랜잭션 확인 |
장애 진단 프로세스
패턴 1: 갑작스러운 Slow Query 증가
→ 원인: 통계 정보 변화, 데이터 분포 변화, 실행 계획 변경
→ 조치: 실행 계획과 실제 row 수 비교, 통계 갱신, 인덱스 검토
패턴 2: 특정 시간대 성능 저하
→ 원인: 배치 작업과 온라인 작업 충돌
→ 조치: 배치 시간 조정, 리소스 분리, 동시 실행 수 제한
패턴 3: 점진적 성능 저하
→ 원인: 데이터 증가, bloat, working set 증가
→ 조치: 파티셔닝, 보관 정책, vacuum/analyze, SQL 재검토
패턴 4: Lock 대기 급증
→ 원인: 긴 트랜잭션, 잘못된 갱신 순서, 대량 DML
→ 조치: 블로커 특정, 트랜잭션 범위 축소, 접근 순서 통일
패턴 5: Connection 부족
→ 원인: 커넥션 풀 설정 부적절, 커넥션 누수, 긴 점유 시간
→ 조치: active/idle/pending 분리 확인, 리크 후보와 slow query 확인DBMS별 핵심 진단 도구 비교
| 기능 | Oracle | MySQL | PostgreSQL |
|---|---|---|---|
| 워크로드 기록 | AWR | Performance Schema digest | pg_stat_statements |
| 실시간 세션 | ASH, v$session | Performance Schema, PROCESSLIST | pg_stat_activity |
| 자동 진단 | ADDM | 제한적, 외부 도구 연계 | 제한적, 확장/외부 도구 연계 |
| Slow Query | AWR SQL 섹션 | Slow Query Log | log_min_duration_statement |
| 락 모니터링 | v$lock, v$session | performance_schema.data_locks | pg_locks |
| 실행 계획 확인 | EXPLAIN PLAN, DBMS_XPLAN | EXPLAIN, EXPLAIN ANALYZE | EXPLAIN (ANALYZE, BUFFERS) |
| 통계 수집 | DBMS_STATS | ANALYZE TABLE | ANALYZE, VACUUM ANALYZE |
| 공간 관리 | DBA_TABLESPACE | information_schema, sys schema | pg_stat_user_tables |
시험에서는 Oracle AWR 리포트의 핵심 확인 항목(Top Timed Events, SQL Elapsed Time), ASH가 활성 세션을 1초 단위로 샘플링한다는 점, Buffer Cache Hit Ratio 계산식이 자주 출제됩니다. 다만 실무에서는 캐시 적중률 하나로 결론을 내리지 않고, DB Time, wait event, 문제 SQL, 실행 계획을 함께 봅니다.
Buffer Cache Hit Ratio 공식:
- Oracle: (1 - physical reads / (db block gets + consistent gets)) * 100
- MySQL: (1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) * 100
- PostgreSQL: heap_blks_hit / (heap_blks_hit + heap_blks_read) * 100
정리
데이터베이스 모니터링의 핵심은 기준선 확보와 변화의 원인 분해입니다. 평소의 TPS, latency, active sessions, lock wait, pool pending, replication lag를 알고 있어야 장애 시 현재 수치가 정상인지 비정상인지 판단할 수 있습니다.
각 DBMS는 고유한 진단 도구를 제공합니다. Oracle의 AWR/ASH/ADDM, MySQL의 Performance Schema와 Slow Query Log, PostgreSQL의 pg_stat_statements와 pg_stat_activity가 대표적입니다. 도구 이름보다 중요한 것은 “어떤 사용자가 느린가 → DB 안에서 무엇을 기다리는가 → 어떤 SQL과 실행 계획이 원인인가 → 조치 후 나아졌는가”의 흐름입니다.
다음 절에서는 애플리케이션과 DB 사이의 ORM과 SQL의 균형을 다루겠습니다.