캘린더에서 실행될 때 데이터베이스 유지보수가 실패합니다. 파편화, 구식 통계, 로그 성장 및 락 경쟁은 쓰기 작업 부하의 함수이지 주간 일정이 아닙니다. 일정된 유지보수는 필요한 테이블을 건너뛰고, 그 결과로 발생하는 사건은 아무도 간극을 알아차리기 전에 발동합니다.
이 기사는 cron 작업을 응답 시스템으로 대체합니다. 네 가지 관찰 가능한 증상(I/O 저하, 쿼리 계획 회복, 저장 공간 압박, 락 경쟁)은 각각 특정 유지보수 근본 원인으로 추적됩니다. SQL Server, PostgreSQL, MySQL에 대한 수정 방법이 포함되어 있습니다. 사전 신호를 생성하지 않는 유일한 실패 모드인 침묵형 손상은 자체 진단 우선 처리를 받습니다. 마무리 점수 카드를 통해 본인이 자가 평가할 수 있습니다.
첫 응답: 엔진 간 대기 상태 트라이액션
느린 쿼리 경고가 발생하면, 엔진에 관계없이 첫 진단 단계는 동일합니다: 쿼리가 기다리고 있는 것을 확인합니다. 대기 상태는 데이터베이스 사고 분류를 위한 보편적인 입구입니다. 이들은 문제가 I/O 제약, 잠금 제약, 또는 CPU 제약인지 알려주며, 이 분류는 이 기사의 어느 섹션에 해결책이 있는지 결정합니다.
SQL Server 대기 유형
PAGEIOLATCH_SH는 쿼리가 디스크에서 버퍼 풀로 데이터 페이지를 읽기 위해 대기하고 있다는 의미입니다. 이는 인덱스 분해구, 버퍼 캐시 압력을 가리킵니다 또는 저장 시스템 포화 상태입니다. LCK_M_S와 LCK_M_X는 동시 트랜잭션 또는 락을 유지하는 유지보수 작업으로부터 행 또는 테이블 수준 락 경쟁을 나타냅니다. CXPACKET (sys.dm_exec_requests에서 확인 가능)는 병렬성 편차 신호를 보내며, 이는 일반적으로 구식 통계 또는 누락된 인덱스로 인해 최적화 프로그램이 비싼 병렬 계획을 선택하게 하는 원인을 추적할 수 있습니다.
PostgreSQL과 MySQL의 대응품
PostgreSQL은 pg_stat_activity을 통해 대기 진단 정보를 노출합니다. 아래 쿼리는 당신의 진단 시작점입니다:
-- PostgreSQL: active session wait events
SELECT pid, wait_event_type, wait_event, state, query
FROM pg_stat_activity
WHERE wait_event IS NOT NULL
AND state != 'idle'
AND backend_type = 'client backend';
위의 다이어그램은 각 값이 그 목표 섹션에 매핑되어 있습니다. 하나의 명확하지 않은 경우를 강조하고자 합니다: a NULL wait_eventstate = 'active'은 쿼리가 계산 제약으로 인해 작동한다는 것을 나타냅니다 (PostgreSQL의 CPU 압력에 해당하는 것), 이는 오래된 통계 또는 계획 회복보다는 I/O를 가리킬 수 있습니다.
MySQL의 경우, performance_schema.events_waits_current은 다이어그램에 표시된 값의 원천입니다.performance_schema = ON을 my.cnf에서 먼저 확인하세요,因为它在MySQL 5.x 빌드의 일부에서 기본적으로 비활성화되어 있으며 0이 아닌 오버헤드를 가지고 있기 때문입니다; MySQL 8.0+에서는 기본적으로 활성화됩니다.SHOW PROCESSLIST는 더 빠르지만 덜 세분화된 관점을 제공합니다.
wait 유형을 식별한 후, 아래의 섹션은 각 카테고리를 유지보수 근본 원인으로 추적하고 수정 방법을 제시합니다. 온프레미스와 클라우드 관리 인스턴스를 포함하는 하이브리드 토폴로지에 대해서는, ManageEngine OpManager Nexus가 두 환경 모두의 wait-state와 slow-query 데이터를 단일 트라이아지 뷰를 통해 노출시킵니다.관리형 데이터베이스의 SaaS 배포.
증상: I/O 저하 및 읽기 증폭
버퍼 캐시 히트 비율이 건강한 OLTP 작업 부하가 유지하는 95-99% 범위 아래로 이동하는 것은 엔진이 메모리보다 더 많은 페이지를 디스크에서 읽고 있음을 엔진 간 신호로 보입니다.
SQL Server 사용자들은 일반적으로 90%을 경고로 보고 85%을 조치 임계값으로 간주합니다. PostgreSQL과 MySQL은 pg_statio_user_tables와 information_schema.INNODB_BUFFER_POOL_STATS (또는 SHOW ENGINE INNODB STATUS)에 해당하는 기능을 제공합니다. 가장 흔한 원인은 인덱스 분할입니다: 페이지가 분할되고, B-tree 잎이 비연속적인 범위에 흩어지며, 하나의 논리적 읽기가 여러 물리적 I/O가 되는 경우입니다. 읽기 증폭은 PAGEIOLATCH가 SQL Server를 기다리면서 나타납니다.DataFileRead PostgreSQL에서, innodb_data_file MySQL을 기다립니다.
클라우드 관리 인스턴스에서 DMV 접근이 제한되어 있을 때 (RDS, Azure SQL Managed Instance), OpManager Nexus의 SaaS 배포는 그 에이전트를 통해 동일한 버퍼 풀 가시성을 노출합니다.
인덱스 팽창 진단
SQL Server: sys.dm_db_index_physical_stats는 분할 데이터의 권위 있는 출처입니다. 아래 쿼리는 5% 이상의 분할 인덱스를 반환하며, 1,000 페이지 이상입니다 (the 페이지 수 필터가 중요합니다 because rebuilding very small indexes produces negligible performance improvement):
SELECT
OBJECT_NAME(ips.object_id) AS tbl_name,
i.name AS idx_name,
ips.index_type_desc,
ips.avg_fragmentation_in_percent,
ips.page_count
FROM sys.dm_db_index_physical_stats(
DB_ID(), NULL, NULL, NULL, 'LIMITED') AS ips
JOIN sys.indexes i
ON ips.object_id = i.object_id
AND ips.index_id = i.index_id
WHERE ips.avg_fragmentation_in_percent > 5
AND ips.page_count > 1000
ORDER BY ips.avg_fragmentation_in_percent DESC;
The'LIMITED' 스캔 모드는 chỉ 인덱스 할당 구조를 순회하므로, 생산 시스템에서 안전하고 빠릅니다. 'SAMPLED'는 매우 큰 테이블이나 파티션 인덱스에서 중간 I/O 비용으로 데이터 페이지의 통계 샘플을 읽어 더 정확한 숫자를 제공합니다. 'DETAILED'는 전체 스캔을 수행합니다; 오프라인 평가를 위해 예약하세요.
PostgreSQL: pg_stat_user_tables 뷰는 첫 번째 신호를 제공합니다. Adead_pct 이 테이블의 10-20% 이상이 고빈도 쓰기 테이블에서는 수동 VACUUM(이 범위는 전문가 지침과 일치하며, 자동 VACUUM 기본값은 20%에서 시작됨):
SELECT schemaname, relname,
n_dead_tup,
n_live_tup,
round(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 2) AS dead_pct,
last_vacuum,
last_autovacuum
FROM pg_stat_user_tables
WHERE n_live_tup > 10000
ORDER BY n_dead_tup DESC
LIMIT 20;
색인 수준의 팽창(물리적 B-tree 팽창은 VACUUM이 회수하지 못함)에 대해서는 pgstattuple 확장이 두 가지 함수를 노출시킵니다. pgstattuple()는free_percent는 PostgreSQL의 avg_fragmentation_in_percent:
CREATE EXTENSION IF NOT EXISTS pgstattuple;
SELECT * FROM pgstattuple('orders_created_at_idx');
pgstatindex()Exit fullscreen modeleaf_fragmentation는 B-tree 특정 메트릭스를 반환합니다: avg_leaf_density (논리적 순서가 아닌 리프 페이지의 비율, 물리적 흩어짐을 나타냅니다)와 __JHSNS_SEG_2223725c_71__ (50% 미만은 인덱스에 많은 비어 있는 페이지가 있다는 것을 나타냅니다):
SELECT * FROM pgstatindex('orders_created_at_idx');
두 함수 모두 대상 관계에 대한 전체 스캔을 수행하므로, 수백 GB 규모의 인덱스에서는 전체 객체의 순차적 읽기와 유사한 실행 시간 및 I/O를 기대해야 합니다. 일반적인 무거운 진단과 같이 스케줄하지만, 뜨거운 루프에 넣지 마세요.
높은 free_percent와 낮은leaf_fragmentationVACUUM으로 대체하여 전체 재건보다는 공간을 회수할 수 있음을 나타낼 수 있습니다. 값의free_percent20-30% 사이에 있는 것들은REINDEX를 위한 널리 사용되는 트리거; 업무 부담량과 현재 커뮤니티 가이드라인을 참고하여 임계값을 조정하세요.
MySQL:쿼리information_schema.TABLESInnoDB 테이블스페이스 분할에 대하여:
SELECT table_schema, table_name,
round(data_length / 1024 / 1024, 2) AS data_mb,
round(data_free / 1024 / 1024, 2) AS free_mb,
round(data_free / (data_length + index_length + data_free) * 100, 2) AS frag_pct
FROM information_schema.TABLES
WHERE engine = 'InnoDB'
AND data_free > 0
ORDER BY data_free DESC
LIMIT 10;
이 지표는 테이블당 테이블 공간(innodb_file_per_table = ON, MySQL 5.6 이후 기본값)과 함께만 의미가 있습니다; 공유 테이블 공간 배포에서는 data_free가 전역 ibdata 파일의 사용되지 않은 공간을 반영하며, 모든 InnoDB 행에 동일하게 반복됩니다.
frag_pct이 20% 이상인 테이블은 일반적으로 후보로 간주됩니다OPTIMIZE TABLE 또는 pt-online-schema-change (이 임계값은 MySQL 문서화된 한계보다는 실무자 지침입니다).
엔진 별 보완 및 다운타임 허용 범위
Microsoft의 인덱스 재정리 및 재건 문서은 파편화 수준을 두 개의 SQL Server 작업에 매핑합니다.
-
5-30% 파편화:
ALTER INDEX idx_name ON tbl_name REORGANIZE는 온라인 작업으로 단계적으로 리프 레벨 페이지를 압축합니다. 실행 중에 중단될 수 있으며 인덱스가 손상되지 않습니다. -
30% 이상:
ALTER INDEX idx_name ON tbl_name REBUILD인덱스를 재생성합니다. 기본적으로 오프라인으로 설정됩니다 (스키마 변경 락을 획득하여 동시 접근을 차단합니다). 추가합니다WITH (ONLINE = ON)는 엔터프라이즈 에디션에서 재건 중 인덱스를 사용할 수 있도록 유지합니다. 온라인 재건조차도 작업의 시작과 끝에서 짧은 스키마 변경 (Sch-M) 락을 획득하며, 일반적으로 밀리초 단위지만, 매우 높은 동시성 작업 부하에서 인지할 수 있는 지연을 유발할 만큼 충분히 길 수 있습니다.
SQL Server 2017+에서는 ONLINE = ON와 RESUMABLE = ON를 결합하고 설정 가능한 값을 사용합니다.MAX_DURATION에서 긴 재건을 일시 중지하고 다시 시작하는 방법: ALTER INDEX idx_name ON tbl_name REBUILD WITH (ONLINE = ON, RESUMABLE = ON, MAX_DURATION = 60). ALTER INDEX idx_name ON tbl_name REBUILD WITH (RESUME)으로 다시 시작합니다. RESUMABLE = ON은 ONLINE = ON이 필요하며, SQL Server 2017에서는 Enterprise 버전만 지원합니다; SQL Server 2019+에서는 Standard 및 Web 버전에서도 지원하므로, 이 구문을 스크립팅하기 전에 버전을 확인하세요.
The 5% 기준은 동일하게 중요합니다 3% 파편화된 인덱스에서 REORGANIZE를 실행하면 로그 활동이 발생하고, I/O를 소모하며, 쿼리 개선 효과를 측정할 수 없습니다.
PostgreSQL에 대해서는, VACUUM 죽은 튜플 저장 공간을 회수하고 가시성 맵을 업데이트합니다.ANALYZE 계획자 통계를 업데이트합니다.REINDEX 물리적 인덱스 팽창이 확인되면 B-tree 구조를 재건합니다
VACUUM VERBOSE ANALYZE transactions;
-- Blocking rebuild (requires maintenance window):
REINDEX INDEX transactions_created_at_idx;
-- Non-blocking rebuild (PostgreSQL 12+):
REINDEX INDEX CONCURRENTLY transactions_created_at_idx;
REINDEX CONCURRENTLY는 트랜잭션 블록 내에서 실행할 수 없으며 표준 형식보다 더 오래 걸리지만, 재건 중에 쓰기를 계속 허용합니다. 즉각적인 수정 이상으로VACUUM VERBOSE정기적으로 가장 많이 쓰이는 테이블에서 출력을 검토해야 합니다. 이는 죽은 튜플 수, 페이지 재활용 데이터, 정리 통계를 제공하여 테이블 건강에 대한 간접적인 신호를 제공합니다. PostgreSQL의autovacuum은 일반적인 죽은 튜플 정리를 처리합니다.자동으로 동작하지만, 높은 속도의 삭제 작업 부하 하에서는 뒤처질 수 있습니다. 공식 PostgreSQL 문서에서는 일정한 정기 정리에 대한 조정에 대해 설명합니다.autovacuum_vacuum_scale_factor와 autovacuum_vacuum_threshold는 기본값이 너무 엄격한 테이블에 대해 적용됩니다.
MySQL에서는 OPTIMIZE TABLE가 테이블 공간을 재구성하고 통계를 단일 작업에서 다시 빌드합니다. MySQL 8.0 이상에서는 일반 InnoDB 테이블에 대해 이 작업이 온라인으로 실행되며, 준비 및 커밋 단계에서 짧은 메타데이터 락만 잡지만, 큰 테이블의 전체 복사는 상당한 시간이 걸릴 수 있습니다:
OPTIMIZE TABLE events;
ANALYZE TABLE events;
내부적으로, InnoDB는 OPTIMIZE TABLE를 ALTER TABLE ... FORCE으로 매핑하고 클러스터 인덱스와 모든 보조 인덱스를 재건합니다. 큰 테이블에서 절대 중단 없는 실행을 위해 Percona Toolkit의 pt-online-schema-change는 원본 테이블을 살리면서 동일한 재건을 수행합니다:
pt-online-schema-change \
--alter "ENGINE=InnoDB" \
--execute \
D=app_prod,t=events,h=127.0.0.1,F=$HOME/.my.cnf
이는 그림자 사본을 유지하고 재건 중에 트리거를 통해 쓰기를 다시 재생합니다. --execute 플래그가 필요합니다; 없으면 도구는 테스트 모드만 실행됩니다.
증상 심각성별 수정 검색:
| 증상 심각성 | 엔진 | 운영 중지 허용 시간 | 권장 조치 |
|---|---|---|---|
| 가벼움 (frag < 5% / dead_pct < 10%) | 모든 | N/A | 없음 |
| 중간 (5-30%) | SQL Server | 어떤 | ALTER INDEX ... REORGANIZE |
| 심각 (> 30%) | SQL Server | 필수 | 인덱스 ... 재구축 WITH (ONLINE=ON) [Enterprise] |
| 심각 (> 30%) | SQL Server | 가용 | 인덱스 ... 재구축 |
| 고도 (dead_pct > 10%) | PostgreSQL | 모든 | 비우기 분석 |
| 높은 비대화 (free_percent > 30%) | PostgreSQL | 필요 | REINDEX CONCURRENTLY |
| 높춰짐 (frag_pct > 20%) | MySQL | 사용 가능 | OPTIMIZE TABLE |
| 높은 (파티션 비율 > 20%) | MySQL | 필수 | pt-online-schema-change |
파티션 문제를 해결한 후, 느린 쿼리를 유발하는 다음 실패 카테고리는 구식 통계입니다. 이는 최적화기가 인덱스 탐색이 순서 수준 더 빠른 스캔을 선택하게 만듭니다.
증상: 쿼리 계획 회복
실행 계획에는 어제 인덱스 탐색이 실행된 테이블 스캔이 표시됩니다. 최적화기는 변경되지 않았지만, 그것이 의존하는 데이터는 변경되었습니다. 이것은 통계 문제입니다.
고정된 통계 진단
SQL Server 최적화기는 행 수 추정치와 데이터 분포 히스토그램을 사용합니다 인덱스 검색과 테이블 스캔 중 선택해야 합니다. 빠르게 성장하는 테이블에서 이러한 통계가 몇 주 동안 최신이 아니면 최적화 프로그램은 검색을 선택하지만, 검색은 훨씬 빠를 수 있습니다. 대량의 배치 로드를 받는 모든 테이블에 UPDATE STATISTICS table_name WITH FULLSCAN을 실행하세요. WITH SAMPLE 변형은 행 샘플링 비율을 사용하여 큰 테이블에서 치우친 분포를 놓칠 수 있으며, 최신처럼 보이지만 대표적인 하위 집합을 반영하지 않는 통계를 생성합니다.
최신 통계 또는 나쁜 계획 선택으로 인해 고통받는 인덱스를 감지하려면 sys.dm_db_index_usage_stats를 쿼리하세요:
SELECT OBJECT_NAME(object_id) AS tbl_name,
index_id,
user_seeks,
user_scans,
user_lookups
FROM sys.dm_db_index_usage_stats
WHERE database_id = DB_ID()
ORDER BY user_scans DESC;
최소 검색이지만 높은 스캔을 가진 인덱스는 통계 업데이트 또는 누락된 인덱스 평가 후보입니다.
PostgreSQL의 ANALYZE 명령와 MySQL의ANALYZE TABLE독립적으로 플래너 통계를 업데이트합니다.VACUUM그리고OPTIMIZE TABLE각각으로. PostgreSQL에서는 자동백업이 실행됩니다.ANALYZE자동으로 이후에조정 가능한 행의 비율 변경(제어되어)autovacuum_analyze_scale_factor, 기본값 0.1 또는 10%일 수 있지만, 그 기본값은큰 테이블에는 너무 높습니다은 2억 행 테이블은 자동 비우기의 ANALYZE 단계를 트리거하기 위해 200만 행 변경이 필요하며, 그때까지 쿼리 계획은 몇 시간 동안 잘못되었을 수 있습니다. autovacuum_analyze_scale_factor을 0.01로 낮추거나 autovacuum_analyze_threshold을 사용하여 테이블별 오버라이드를 처리하면 이를 해결할 수 있습니다.
업데이트 통계를 중단 없이
SQL Server에서UPDATE STATISTICS는 일반적으로 쿼리를 차단하지 않습니다 (데이터 읽기는 NOLOCK 심맥스로 실행됩니다), 하지만 높은 작업 부하 시나리오에서 비동기 통계 업데이트는 쿼리 컴파일 중 짧은 스키마 락 경쟁을 유발할 수 있습니다. 그러나 영향을 받는 테이블에 대해서는 캐시된 실행 계획을 무효화합니다: 이후 즉시, SQL Server는 업데이트된 테이블에 대해 다음 실행 시 계획을 다시 컴파일할 것입니다, 이는 동시 쿼리가 많은 시스템에서 CPU를 잠시 높일 수 있습니다. 업데이트된 테이블에 대해 트래픽이 적은 시간 동안 실행하세요. 선택 사항은FULLSCAN와 SAMPLE는 테이블 크기와 분포 왜곡에 따라 달라집니다.
작은 중간 크기의 테이블의 경우, FULLSCAN는 보통 피크 시간 외에 실행할 만큼 빠르게 완료됩니다(실질적인 상한은 하드웨어에 따라 다르지만, 많은 팀이 대략 100만 행을 규칙의 끝점으로 사용합니다). 더 큰 테이블의 경우, 더 높은 샘플 비율(예: SAMPLE 20 PERCENT 또는SAMPLE 30 PERCENT은 기본 샘플보다 정확성과 지속 시간 간의 균형이 더 좋지만, 최적의 비율은 작업 유형에 따라 다릅니다.
PostgreSQL에서는 ANALYZE이 조정 가능한 샘플을 읽습니다 (기본값은 default_statistics_target = 100로, 즉 각 열당 30,000 행을 의미하며, 테이블을 잠그지 않습니다. 대량 로드 또는 파티션 교체 후 수동으로 실행하세요.
MySQL에서는ANALYZE TABLE는 InnoDB에서 인덱스 트리의 랜덤 샘플을 읽는 가벼운 작업입니다. 빠른 작업입니다: MySQL 8.0+에서는 ANALYZE TABLE가 온라인 DDL 시맨틱스를 사용하여 이전 버전이 요구했던 전체 읽기 락을 피합니다. EXPLAIN를 대표적인 쿼리 전후에 캡처하여 플래너가 새로운 통계를 받아들였는지 확인하세요.
OpManager Nexus는 온프레미스에서 역사적 기준선 비교 및 이상 표시를 통해 쿼리 계획 회복을 자동으로 감지합니다. 동일한 기능은 SaaS 배포를 통해 클라우드 관리 데이터베이스로 확장되며, 느린 쿼리 로그 분석는 설정 가능한 실행 시간 임계값을 초과하는 쿼리에 대한 자세한 분석을 수행합니다. 아래 자동 수정 섹션에서는 감지를 수정 워크플로우에 연결하는 방법을 설명합니다.
통계 실패는 쿼리 계획이 저하될 때까지 보이지 않습니다. 저장 실패는 동일하게 조용합니다, 디스크가 가득 찼고 데이터베이스가 오프라인 상태가 될 때까지.
증상: 저장 압력과 무제한 성장
85% 용량에서 디스크 사용량 경고가 발생했습니다. 데이터베이스 서버는 몇 달 동안 로그 파일이나 테이블 공간이 얼마나 빨리 증가하는지 확인하지 않고 작동해 왔습니다. 근본 원인은 두 가지 범주로 나뉩니다: 관리되지 않은 트랜잭션 로그 성장과 아카이빙 전략의 부재입니다. 둘 다 모니터링이 몇 주 전에 포착했어야 할 유지보수 실패입니다.
트랜잭션 로그 및 WAL 관리
SQL Server:A정상 복구 모델 데이터베이스에 정기적인 트랜잭션 로그 백업 없음로그 파일을 디스크가 가득 찰 때까지 성장시킬 것이며, 가득 찬 데이터 볼륨은 즉각적인 생산 중단을 유발합니다. 모든 데이터베이스의 현재 로그 공간 사용량을 확인하려면 실행하세요DBCC SQLPERF(LOGSPACE);, 각 데이터베이스에 대해 로그 크기, 사용된 공간 백분율, 상태를 반환합니다. 단일 데이터베이스에 대해 쿼리sys.databases포트맨저님께서는log_reuse_wait_desc 열은 로그가 잘리지 않는 정확한 이유를 설명합니다 (예: LOG_BACKUP, ACTIVE_TRANSACTION). 로그 백업을 복구 지점 목표(RPO)에 맞는 간격으로 예약하세요: 대부분의 OLTP 작업 부하에서는 5-30분 사이의 간격이 일반적으로 사용되며, 높은 트랜잭션 시스템에는 더 짧은 간격이 사용됩니다. 그러나 올바른 빈도는 작업 부하에 특화되어 있습니다.
DBCC SHRINKFILE 로그 파일에 있는 것은 예기치 않은 로그 성장 이벤트 후 공간을 회복하기 위한 마지막 수단입니다. 일상적인 정리 도구가 아닌 마지막 수단인 이유는 가상 로그 파일에 미치는 부작용 때문입니다. (VLFs), SQL Server가 트랜잭션 로그를 나누는 내부 구성 요소들입니다. 각 축소-다시 성장 주기는 새로운 VLF를 추가하므로 반복적으로 축소된 로그는 몇 개의 큰 VLF 대신 많은 작은 VLF로 분할됩니다. 그 분할은 순차적 로그 쓰기 성능을 저하시키고 복구 시간을 증가시킵니다.는 주된 원인을 해결하는 것이지 (로그 백업 누락, 오래 걸리는 트랜잭션) 일정에 따라 축소하는 것이 아닙니다.
PostgreSQL: WAL (Write-Ahead Log) 관리는 SQL Server의 트랜잭션 로그와 동일한 기능을 수행합니다. archive_mode 및 archive_command 설정은 완료된 WAL 세그먼트가 아카이브 스토리지로 전송되는지 여부를 제어합니다. 아카이빙이 활성화되지 않으면 WAL 세그먼트는 에서 pg_wal/ 에 쌓이게 됩니다. wal_keep_size 매개변수 (PostgreSQL 13+, wal_keep_segments 을 대체)는 유지할 WAL 데이터의 하한을 설정하지만 성장을 제한하지 않습니다. 생산 시스템의 경우, archive_mode = on 으로 지속적인 아카이빙을 구성하고 지정하세요.archive_command백업 인프라스트럭처로 (pgBackRest, Barman, 또는 클라우드 네이티브 대체품)
아카이빙이 활성화되고 최신 상태인지 확인하려면:SELECT * FROM pg_stat_archiver;체크last_archived_wal타임스탬프와failed_count0이 아닌failed_count또는 썩은last_archived_timeWAL 세그먼트가 축적되고 있습니다. 또한:SELECT count(*), pg_size_pretty(sum(size)) FROM pg_ls_waldir();(PostgreSQL 10+)은 총 WAL 디렉토리 크기를 표시합니다.
MySQL: 이진 로그 (binlogs)는 복제 및 포인트 인 타임 복구를 제공합니다. 회전 없이는 무한히 증가합니다. expire_logs_days (MySQL 8.0.3에서 폐지됨) 또는 binlog_expire_logs_seconds (MySQL 8.0+)는 자동 정리를 제어합니다. binlog_expire_logs_seconds = 604800를 설정하면 7일간의 이진 로그를 유지하므로 대부분의 복제 토폴로지에 충분합니다. PURGE BINARY LOGS BEFORE NOW() - INTERVAL 7 DAY을 실행하여 단회 정리를 수행합니다.
OpManager Nexus를 이용한 용량 예측
85%의 디스크 경고에 반응하는 것은 계획된 조치를 취할 여지를 거의 남기지 않습니다. OpManager Nexus의AI/ML 기반 저장소 예측최대 14일의 이력을 사용하여 언제 발생할지 예측합니다저장 공간이 80%, 90%, 그리고 100%에 도달할 것입니다., 최소 3일의 데이터가 있으면 팀에 "N일 안에 디스크가 가득 찼습니다" 신호를 보냅니다. 그것은 적응형 임계값 실제 이상 징후에 알림이 트리거되도록 기본값 행동을 학습하고, 데이터베이스 탭은 개별 데이터베이스 크기, 데이터 및 로그 파일 활용률 및 성장 추세를 표시합니다.
참고: OpManager Nexus의 자체 모니터링 데이터 보관 (설정 >> 일반 설정 >> 데이터베이스 유지보수)는 생산 데이터베이스 저장소와 독립적입니다. 기본값은 상세, 시간별, 일별 통계에 대해 7일, 30일, 365일입니다.
OpManager Nexus의 예측 보고서를 사용하여 아카이빙 주기가 성장에 맞춰지는지 확인하세요: 예측이 30일 후 80%의 용량을 보여주지만 아카이브 작업이 매월 실행되면, 빈도를 증가시키거나 더 많은 저장 공간을 제공하세요.
저장 압력은 시간이 지남에 따라 축적되는 수동적인 실패입니다. 락 경쟁은 능동적인 실패입니다: 데이터베이스를 수정하려는 유지보수 작업이 사건의 원인이 되었습니다.
증상: 유지보수 작업에서 발생한 락 경쟁
예약된 유지보수 작업 직후 차단된 세션의 급증은 REBUILD 또는 REORGANIZE가 생산 트래픽과 충돌했으며는 잠금 경쟁을 생성했습니다. 유지보수 작업은 성능을 개선해야 하지만, 최고 트래픽 시나 유지보수 창 없이 ONLINE = ON 없이 실행되는 인덱스 REBUILD는 동시 쿼리를 차단하는 잠금을 획득하여 수정 작업이 사건으로 전환됩니다
. 유지보수 유발 차단을 식별합니다
유지보수 시간을 OpManager Nexus의 세션 탭과 연관시키는 것이 유지보수로 인한 차단을 애플리케이션 수준의 경쟁과 구별하는 방법입니다. 유지보수 창이 열린 후 몇 분 안에 차단된 세션 수가 급증하면 유지보수 작업이 원인입니다. SQL Server에서는 sys.dm_exec_requests에서 wait_type 값이 LCK_M_*로 시작하는 세션을 확인한 다음, 체인의 머리를 차단하는 요소를 찾아 검사하세요.command 열은 ALTER INDEX 또는 DBCC 작업에 사용됩니다.
PostgreSQL에서 pg_stat_activity은 Lock 대기 이벤트를 표시하며 wait_event 값으로 relation 또는 transactionid와 같은 값을 가집니다. 만약 블로킹 PID가 REINDEX을 실행 중이라면VACUUM FULL는 유지보수 유발된 경쟁입니다. 클라우드 관리 인스턴스에서 세션 탭 접근이 불가능한 경우, OpManager Nexus의 SaaS 배포는 잠금 경쟁을 표시하고 동일한 트라이어지 신호에 대해 데이터베이스 성능 대시보드에서 차단 세션 수를 표시합니다.
온라인 및 중단 가능한 작업
수정이 작동 중입니다: 온라인 작업을 사용하고 피크 트래픽 시간 외에 일정을 잡으세요.
SQL Server: I/O 저하 섹션에서 설명한 대로 ALTER INDEX ... REBUILD WITH (ONLINE = ON, RESUMABLE = ON, MAX_DURATION = 60)를 사용하세요. 지속 시간은 분 단위의 양의 정수이며, 유지보수 시간에 따라 설정하세요.REORGANIZE는 항상 온라인이며 중단될 수 있습니다.
PostgreSQL: REINDEX INDEX CONCURRENTLY (I/O Degradation 섹션에서 소개됨)은 배타적 락을 피합니다. VACUUM는 FULL 없이 읽기 또는 쓰기를 차단하지 않습니다.
MySQL: 표준 OPTIMIZE TABLE는 MySQL 8.0+에서 이미 온라인 DDL로 실행됩니다 (I/O Degradation 섹션에서 소개됨). 사용하세요pt-online-schema-change 테이블이 매우 큰 경우 잠금 지속 시간에 더 세밀한 제어가 필요하거나, OPTIMIZE TABLE가 제공하지 않는 트리거된 그림자 사본 의미론이 필요할 때,
위에 언급된 네 가지 증상 카테고리는 모두 아웃아가이전이 발생하기 전에 관찰할 수 있는 성능 신호를 생성합니다. 손상은 다릅니다: 그것은 쿼리 실패나 데이터 손실로 표면화될 때까지 신호를 생성하지 않습니다.
증상: 침묵 손상 및 무결성 실패
부정부패는 사전 대기 이벤트나 지연 이동을 생성하지 않으므로, 검출은 의도된 예약된 행위이며 경보 응답이 아닙니다. 정기적인 무결성 검사는 주요 검출 메커니즘이며, 저장 수준 체크섬, 페이지 검증, 신뢰할 수 있는 백업으로 보완됩니다.
SQL Server: DBCC CHECKDB는 페이지 손상, 할당 오류, 일관성 위반를 포착합니다.
-- Recommended production form: suppresses informational messages, shows only errors
DBCC CHECKDB('ProductionDB') WITH NO_INFOMSGS, ALL_ERRORMSGS;
큰 데이터베이스에서 전체 DBCC CHECKDB가 유지보수 창에 너무 느리면, DBCC CHECKDB ... WITH PHYSICAL_ONLY 논리적 일관성 검사 없이 페이지와 레코드 헤더 무결성을 검사하여 훨씬 빠르게 완료합니다. 손상은 SQL Server 오류 로그에 메시지로 Msg 823, 824 또는 825로 나타납니다는 알려진 손상 사건을 적극적으로 확인하려면 의심되는 페이지 테이블을 쿼리하세요:
SELECT db_id, file_id, page_id, event_type, error_count, last_update_date
FROM msdb.dbo.suspect_pages
WHERE event_type IN (1, 2, 3);
이벤트 유형 1 = 823/824 오류, 2 = 잘못된 체크섬, 3 = 찢어진 페이지. 비어 있지 않은 결과는 즉시 DBCC CHECKDB와 복원 계획이 필요합니다.
DBCC CHECKDB를 유지보수 창에 따라 가능한 한 자주 실행하는 것이 안전한 방법입니다. 많은 전문가들이 모든 데이터베이스에 매일 권장합니다; 그것이 실현하기 어렵다면 중요한 데이터베이스를 우선시하고 WITH PHYSICAL_ONLY를 사용하여 큰 데이터베이스의 간격을 단축하세요.
PostgreSQL:pg_amcheck 유틸리티 (PostgreSQL 14+)는 인덱스 항목을 참조하는 모든 힙 튜플이 실제로 존재하고 인덱스 항목이 올바른 정렬 순서에 있는지 확인하여 B-tree 인덱스의 무결성을 검증합니다. 기본 호출은 일상적인 예약된 검사에 충분히 빠르며 대부분의 손상을 포착합니다:
pg_amcheck mydb
예상치 못한 충돌, 저장소 이벤트 또는 복제 실패 후, 중요 테이블에 대해 철저한 변형을 실행하세요:
pg_amcheck --heapallindexed --parent-check mydb
--heapallindexed는 모든 힙 튜플에 해당 인덱스 항목이 있는지 더 깊은 검사를 수행합니다;--parent-check크로스 레벨 B-트리 불변량을 확인합니다. 두 플래그는 실행 시간을 크게 증가시키므로, 이를 사고 대응 또는 사건 후 검증에 예약하고 일상적인 일정보다는 사용하세요.
MySQL: mysqlcheck테이블 수준의 무결성 검증을 제공합니다:
mysqlcheck --check --all-databases -u root -p
개별 테이블에 대해서는CHECK TABLE table_name는 MySQL 클라이언트 내에서 동일한 작업을 수행합니다. InnoDB 테이블은 주요 버전 업그레이드 후 CHECK TABLE ... FOR UPGRADE를 통해 저장 형식 호환성을 확인할 수 있습니다.
이러한 검사를 수동으로 수행하는 것이 안전 장치입니다. 다음 섹션에서는 플랫폼이 대기 중인 엔지니어가 로그인하기 전에 자동으로 응답을 자동화하는 방법을 보여줍니다.
경고에서 수정까지: 엔진 간 자동 수정
3시에 경보가 발생할 때, 플랫폼이 보완 조치를 자동으로 실행하는 것이 해결 방법을 알아보는 것보다 훨씬 중요합니다. OpManager Nexus의 IT 워크플로우 자동화는 경보 임계값이 벗어나면 사용자 지정 모니터링 스크립트를 트리거합니다: 스크립트는 증상의 진단 표면(파편화, 죽은 튜플, 로그 공간)을 쿼리하고 심각도를 평가한 후 보완 조치를 실행합니다.
SQL Server: OpManager Nexus에 보완 조치를 연결합니다
OpManager Nexus는 PowerShell 또는 shell 스크립트를 사용자 지정 모니터로 사용합니다 (사용자 지정 스크립트 모니터는 빌드 12.7 이상이 필요합니다). 통합 패턴은 아래의 PostgreSQL 및 MySQL 예제와 일치합니다: sys.dm_db_index_physical_stats로 분할을 쿼리하고, 임계값에서 분기하며, ALTER INDEX REORGANIZE를 발행합니다.REBUILD WITH (ONLINE = ON)에 따라, 하나의 작업마다 하나의 로그 라인을 발행하여 실행이 모니터의 이력에 표시되도록 합니다. 대상 데이터베이스에 최소 db_ddladmin를 가진 서비스 계정 아래에서 스크립트를 실행하십시오; SQL 인증 또는 교차 도메인 설정의 경우, 비밀 정보 저장소에서 자격 증명을 가져오십시오.
PostgreSQL 및 MySQL 셸 자동화
PostgreSQL에 대해서는, cron으로 작동하는 셸 스크립트가 질의할 수 있습니다pg_stat_user_tables는 팽창된 테이블과 트리거 수정을 위한 것입니다:
#!/usr/bin/env bash
# PostgreSQL automated vacuum/reindex for tables exceeding dead tuple threshold.
# Credentials sourced from ~/.pgpass (chmod 600); export PGPASSFILE if non-default.
PGHOST="localhost"
PGPORT="5432"
PGDATABASE="app_prod"
PGUSER="maintenance_user"
export PGPASSFILE="${PGPASSFILE:-$HOME/.pgpass}"
DEAD_THRESHOLD=15
BLOAT_THRESHOLD=30
# VACUUM tables with high dead tuple ratio
psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d "$PGDATABASE" -t -A -F'|' -c "
SELECT schemaname, relname, round(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 2)
FROM pg_stat_user_tables
WHERE n_live_tup > 10000
AND round(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 2) > $DEAD_THRESHOLD
" | while IFS='|' read -r schema table dead_pct; do
echo "$(date '+%Y-%m-%d %H:%M:%S') | VACUUM ANALYZE ${schema}.${table} | dead_pct=${dead_pct}%"
psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d "$PGDATABASE" -c "VACUUM ANALYZE ${schema}.${table};"
done
MySQL에 대해서는 유사한 접근 방식으로 information_schema.TABLES을 쿼리하고 OPTIMIZE TABLE을 트리거합니다. 스크립트에 자격 증명을 내장하기 대신 MySQL 옵션 파일을 사용하세요 ( ~/.my.cnf을 [client] 자격 증명으로 만들고 권한을 600으로 제한하세요):
#!/usr/bin/env bash
# MySQL automated optimize for InnoDB tables exceeding fragmentation threshold
MYSQL_HOST="localhost"
MYSQL_DB="app_prod"
FRAG_THRESHOLD=20
mysql --defaults-extra-file="$HOME/.my.cnf" -h "$MYSQL_HOST" -N -B -e "
SELECT table_name, round(data_free / (data_length + index_length + data_free) * 100, 2) AS frag_pct
FROM information_schema.TABLES
WHERE table_schema = '${MYSQL_DB}'
AND engine = 'InnoDB'
AND data_free > 0
AND round(data_free / (data_length + index_length + data_free) * 100, 2) > ${FRAG_THRESHOLD}
" | while read -r table frag_pct; do
echo "$(date '+%Y-%m-%d %H:%M:%S') | OPTIMIZE TABLE ${table} | frag_pct=${frag_pct}%"
mysql --defaults-extra-file="$HOME/.my.cnf" -h "$MYSQL_HOST" "$MYSQL_DB" -e "OPTIMIZE TABLE ${table};"
done
cron을 통해 스크립트를 일정에 추가하고 (예: 0 3 * * * /opt/scripts/pg_maintenance.sh >> /var/log/db_maintenance.log 2>&1), OpManager Nexus의 커스텀 모니터 통합을 통해 로그 출력을 모니터링합니다.
클라우드 관리 데이터베이스 자동화
Amazon RDS, Aurora, 또는 Azure SQL에서 실행되는 데이터베이스에 대해 OpManager Nexus의 SaaS 배포는 위에 언급된 PowerShell 및 셸 자동화 패턴의 클라우드 측 대응품을 제공합니다. 그것의 IT 자동화 모듈은 임계값 위반 및 이상 검출에서 수정 작동을 트리거하고, AI로 지원되는 기준선 자체 관리 인스턴스에 필요한 수동 임계값 조정을 대체합니다. RDS에 특히, 서비스 작업 시작, 중지 및 장애 조치 재부팅이 직접 노출됩니다. 엔진 특정 모니터 설정는 SQL Server, PostgreSQL, 및 MySQL용으로 별도로 문서화되어 있습니다. 임계값 프로파일은 개발, 스테이징, 및 운영 모니터링에 동일한 경보 구성을 적용할 수 있게 해주므로, 실제 스테이징 부하 하에서 인덱스를 분할하는 쿼리가 생산 환경 규모에 도달하기 전에 느린 쿼리 검출에 노출됩니다.
유지보수 건강 스코어카드: 현재 포지션 평가
초기 진단 질문을 처음부터 실행하기보다 이 스코어카드를 사용하여 유지보수 포지션을 평가하세요. 각 항목은 위에 해당 섹션에서 다루었던 진단 방법을 참조합니다.
I/O 건강 상태 (참조: I/O 저하 섹션)
- [ ] SQL Server:
sys.dm_db_index_physical_stats쿼리를 실행하세요 (결과를 30% 분할로 필터링). 반환된 인덱스 수: ___ - PostgreSQL:
pg_stat_user_tables죽은 튜플 쿼리를 실행하세요. dead_pct가 10-20% 이상인 테이블들은 즉각적인 주의 대상입니다: ___ - MySQL:
information_schema.TABLES분할 쿼리를 실행하세요. frag_pct가 20% 이상인 테이블들: ___
통계 신선도 (참조: 쿼리 계획 회귀 섹션)
- [ ] SQL Server: 확인하세요
sys.dm_db_index_usage_stats인덱스에 대해 제로 seeks지만 높은 scans가 있는 경우 (계획 회복 또는 잘 맞지 않는 인덱스) - [ ] PostgreSQL: 1천만 행 이상의 테이블에 대해
autovacuum_analyze_scale_factor가 0.1보다 낮게 설정되었는지 확인 - [ ] MySQL: 쓰기량이 높은 상위 10개 테이블에 대해
ANALYZE TABLE를 실행; 대표적인 쿼리 전후로EXPLAIN출력을 캡처하여 계획자 통계가 예상대로 변경되었는지 확인
저장 경로 (참조: 저장 압력 섹션)
- [ ] OpManager Nexus 예측 보고서는 임계점 통과 전에 충분한 용량 여유가 있는지 확인됨: 예 / 아니오
- [ ] 트랜잭션 로그 백업 작업 (SQL Server) 또는 WAL 아치iving (PostgreSQL)이 실행 중이며 마지막 백업이 확인되었음: 예 / 아니오
- [ ] 바이너리 로그 회전 (MySQL)이 설정됨
binlog_expire_logs_seconds명시적 값으로 설정됨: 예 / 아니오
무결성 기준선 (참고: 침묵된 손상 섹션)
- [ ] SQL Server:
DBCC CHECKDB중요 데이터베이스에서 마지막 실행 날짜: ___ - [ ] PostgreSQL:
pg_amcheck마지막 실행 날짜 (또는 동일한 수동 검사): ___ - [ ] MySQL:
mysqlcheck --check마지막 실행 날짜: ___
자동화 커버리지 (참조: 자동화 수정 섹션)
- [ ] 최소 하나의 자동화 수정 스크립트가 배포되고, 일정이 설정되며, 출력 로그를 생성하고 있는지 확인되었습니다: 예 / 아니오
- [ ] OpManager Nexus 경고 임계값이 주요 데이터베이스 건강 지표(BCHR, 디스크 사용률, 차단된 세션)에 대해 구성되고 테스트되었습니다: 예 / 아니오
- [ ] 유지보수 시간은 모니터링 신호에 따라 일정이 잡힙니다. 달력 날짜가 아닙니다: 예 / 아니오
OpManager Nexus의 느린 쿼리 및 세션 데이터(온프레미스 성능 탭 또는 SaaS 데이터베이스 메트릭 대시보드)와 결과를 비교해 보세요. 크기 순위 결과에서 테이블이 느린 쿼리 감지의 원인으로도 나타나면, 그것이 당신의 최우선 유지보수 대상입니다.











