DBAtlas · 데이터베이스 운영 아틀라스

데이터베이스가 느림

증상

애플리케이션 전반의 느려짐. 쿼리가 평소보다 훨씬 오래 걸리고 사용자 불만이 제기된다.

1. 먼저 정확히 무엇이 느린지 명확히 한다

전부 느린가, 특정 쿼리/화면만 느린가? 언제 시작되었으며 배포/패치/데이터 증가와 시기가 겹치는가? 시스템 전체인가, 단일 DB인가? 이를 통해 조사를 CPU, I/O, 락, 계획 변경 중 하나의 축으로 좁힌다.

2. 활성 세션과 대기 이벤트를 확인한다

지금 무엇이 실행 중이고 무엇이 대기 중인가? 지배적인 wait event(I/O, 락, 로그)가 병목을 가리킨다.

Oracle

SELECT sid, username, status, event, sql_id FROM v$session WHERE status='ACTIVE' AND type='USER';

PostgreSQL

SELECT pid, state, wait_event_type, wait_event, query FROM pg_stat_activity WHERE state='active';

MySQL

SELECT id, user, command, time, state, info FROM information_schema.processlist WHERE command<>'Sleep' ORDER BY time DESC;

SQL Server

SELECT session_id, status, wait_type, wait_time, blocking_session_id FROM sys.dm_exec_requests WHERE session_id IN (SELECT session_id FROM sys.dm_exec_sessions WHERE is_user_process = 1);

MongoDB

db.currentOp({ active: true, secs_running: { $gt: 1 } })

Redis

SLOWLOG GET 10

3. 블로킹을 확인한다

느려짐의 가장 흔한 원인은 한 세션이 다른 세션을 막는 것이다. 블로킹 세션을 찾아 체인의 시작점을 식별한다.

Oracle

SELECT blocking_session, sid, seconds_in_wait, event FROM v$session WHERE blocking_session IS NOT NULL;

PostgreSQL

SELECT pid, pg_blocking_pids(pid) AS blocked_by, query FROM pg_stat_activity WHERE cardinality(pg_blocking_pids(pid))>0;

MySQL

SELECT * FROM sys.innodb_lock_waits\G

SQL Server

SELECT session_id, blocking_session_id, wait_type FROM sys.dm_exec_requests WHERE blocking_session_id<>0;

4. 가장 비용이 큰 쿼리를 추출한다

리소스를 가장 많이 소비하는 상위 10개 쿼리를 찾는다. 대개 문제의 90%는 소수의 쿼리에서 비롯된다.

Oracle

SELECT sql_id, elapsed_time/1e6 sec, executions, sql_text FROM v$sql ORDER BY elapsed_time DESC FETCH FIRST 10 ROWS ONLY;

PostgreSQL

SELECT calls, total_exec_time, mean_exec_time, query FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;

MySQL

SELECT digest_text, count_star, sum_timer_wait/1e12 sec FROM performance_schema.events_statements_summary_by_digest ORDER BY sum_timer_wait DESC LIMIT 10;

SQL Server

SELECT TOP 10 total_elapsed_time/execution_count avg_us, execution_count, SUBSTRING(t.text,1,200) FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) t ORDER BY total_elapsed_time DESC;

5. 의심 쿼리의 계획을 분석한다

느린 쿼리의 EXPLAIN을 확인한다. full table scan, 누락된 인덱스, 잘못된 조인 순서, 오래된 통계를 찾는다. 계획이 갑자기 바뀌었다면 통계 수집을 고려한다.

Oracle

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', NULL, 'ALLSTATS LAST'));

PostgreSQL

EXPLAIN (ANALYZE, BUFFERS) <query>;

MySQL

EXPLAIN ANALYZE <query>;

SQL Server

SET STATISTICS PROFILE ON; <query>; SET STATISTICS PROFILE OFF;

MongoDB

db.my_collection.find({...}).explain('executionStats')  // COLLSCAN or IXSCAN?

6. 시스템 리소스를 확인한다

OS 수준에서 CPU, I/O 대기(iowait), 메모리, 디스크 사용량을 확인한다. cache hit ratio를 확인하고, 낮으면 메모리 병목일 수 있다. 문제가 DB 외부에 있을 수도 있다(디스크 가득 참, noisy neighbor VM).

7. 결정: 임시 조치인가 근본 해결인가

즉각적인 완화: 폭주 쿼리 종료, 누락된 인덱스 추가, 통계 수집. 근본 해결: 쿼리/인덱스 재설계, 메모리 설정 수정, 계획 안정성 확보(baseline/hint).

DBAtlas에서 열기 →