DB 인덱스 재구성 시 테이블 잠금과 서비스 영향
증상 확인: 인덱스 재구성 중 발생하는 성능 저하와 타임아웃
데이터베이스에서 인덱스 재구성 작업을 실행한 후, 애플리케이션 로그에 갑자기 “쿼리 타임아웃” 또는 “잠금 대기 시간 초과” 오류가 빈번하게 발생하기 시작했습니다, 사용자들은 웹 페이지 로딩이 극도로 느려지거나, 특정 트랜잭션(주문 처리, 데이터 저장 등)이 멈춘 것처럼 보이는 현상을 경험합니다. 서버 모니터링 도구를 확인하면, 해당 테이블에 대한 LCK_M_X 또는 LCK_M_S와 같은 잠금(Lock) 대기 이벤트가 급증해 있는 것을 확인할 수 있습니다. 이는 인덱스 재구성 작업이 예상보다 강력한 잠금을 설정하여, 정상적인 비즈니스 쿼리까지 차단하고 있음을 의미합니다.

원인 분석: 왜 인덱스 재구성은 테이블을 잠그는가?
인덱스 재구성(REBUILD)은 단순한 조정이 아닌, 인덱스의 물리적 구조를 새로 만드는 작업입니다. 이 과정에서 데이터 페이지의 논리적 순서를 물리적 순서와 일치시키기 위해 기존 인덱스를 삭제하고 새로 생성해야 합니다, 데이터베이스 엔진은 이 작업 동안 데이터의 무결성을 보장하기 위해, 작업 대상이 되는 테이블 또는 인덱스에 대해 높은 수준의 잠금(주로 배타적 잠금)을 획득합니다. 이 잠금이 유지되는 동안에는 해당 객체에 대한 대부분의 읽기 및 쓰기 작업이 대기 상태에 빠지게 됩니다. 문제는 이 잠금 지속 시간이 테이블의 크기와 서버의 I/O 성능에 비례하여 길어질 수 있다는 점입니다. 수십 GB 이상의 대형 테이블에서 인덱스 재구성을 실행하면 수십 분에서 수시간 동안 서비스 장애가 발생할 수 있습니다.
해결 방법 1: 온라인 인덱스 재구성 사용 (SQL Server, Enterprise Edition)
가장 근본적이고 권장되는 방법은 작업 중에도 기본 테이블에 대한 대부분의 쿼리를 허용하는 온라인 옵션을 사용하는 것입니다. 이 방식은 인덱스를 재구성하는 동안에도 테이블에 대한 업데이트가 가능하도록 설계되었습니다.
-
사용 중인 데이터베이스 버전이 온라인 인덱스 재구성을 지원하는지 확인합니다. SQL Server의 경우 Enterprise, Developer, Evaluation Edition에서 사용 가능합니다.
-
기존의 오프라인 재구성 명령어를 수정합니다. 표준 명령어인
ALTER INDEX [인덱스명] ON [테이블명] REBUILD에 옵션을 추가합니다. -
온라인 재구성을 실행합니다. 명령어는 다음과 같습니다.
ALTER INDEX ALL ON [테이블명] REBUILD WITH (ONLINE = ON, SORT_IN_TEMPDB = ON, MAXDOP = 2);
옵션 설명: ONLINE = ON이 핵심입니다. SORT_IN_TEMPDB = ON은 tempdb를 활용해 작업 속도를 높이고 기본 데이터 파일의 부하를 분산시킵니다. MAXDOP = 2는 병렬 처리 정도를 제한하여 전체 시스템 리소스 소모를 줄이는 데 도움을 줍니다.
주의사항: 온라인 재구성은 오프라인 방식보다 더 많은 로그 공간과 시스템 리소스(I/O, CPU)를 사용합니다. 또한, 작업 시작과 종료 시점에 매우 짧은 스키마 수정 잠금을 필요로 하므로, 그 순간에 실행되는 트랜잭션은 아주 잠깐 대기할 수 있습니다. 작업 전 반드시 충분한 디스크 여유 공간과 로그 백업 계획을 수립해야 합니다.
해결 방법 2: 재구성(REORGANIZE)으로 잠금 최소화
인덱스 재구성(REBUILD)이 불가능한 환경(Standard Edition 등)이거나, 인덱스 단편화가 심하지 않은 경우 더 가벼운 작업인 재구성(REORGANIZE)을 고려합니다. 이 작업은 인덱스의 리프 페이지를 물리적으로 재정리만 하므로 잠금 수준이 낮고 대부분 온라인 상태로 수행됩니다.
-
인덱스 단편화 정도를 먼저 확인합니다. 다음 쿼리를 실행하여
avg_fragmentation_in_percent값을 검토합니다.SELECT * FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('[테이블명]'), NULL, NULL, 'LIMITED'); -
단편화가 5%~30% 사이라면 REORGANIZE가 적합합니다, 30%를 초과하면 rebuild를 고려해야 하지만, 서비스 영향도를 감안해 전략을 세워야 합니다.
-
재구성 명령어를 실행합니다. 이 작업은 페이지 단위 잠금만 사용하므로 블로킹 영향이 매우 적습니다.
ALTER INDEX ALL ON [테이블명] REORGANIZE;
REORGANIZE는 시스템 부하가 적고 잠금 시간이 짧다는 장점이 있지만, 단편화를 완전히 제거하지는 못합니다. 단기적인 성능 회복과 서비스 연속성을 보장해야 할 때 유용한 방법입니다.
해결 방법 3: 작업 분할 및 오프라인 시간대 활용 전략
온라인 옵션이 지원되지 않고, 테이블이 너무 커서 REORGANIZE로는 해결이 안 되는 경우, 전략적인 작업 분할이 유일한 실용적 해결책입니다. 목표는 한 번의 긴 잠금을 여러 번의 짧은 잠금으로 나누는 것입니다.
파티션 인덱스 재구성
테이블이 파티션으로 구성되어 있다면, 파티션 단위로 인덱스를 재구성할 수 있습니다. 이는 작업 부하와 잠금 범위를 특정 파티션으로 국한시킵니다.
-
파티션 번호를 확인합니다.
SELECT * FROM sys.partitions WHERE object_id = OBJECT_ID('[테이블명]'); -
특정 파티션만 재구성합니다. 예를 들어, 1번 파티션만 작업합니다.
ALTER INDEX [인덱스명] ON [테이블명] REBUILD PARTITION = 1; -
서비스에 영향이 적은 시간대(예: 새벽 2시)를 정해, 매일 1~2개의 파티션씩 순차적으로 작업을 진행합니다.
인덱스별 순차 처리 및 유지보수 시간대 설정
파티션이 없을 경우, 테이블의 인덱스를 하나씩 순차적으로 재구성합니다. 모든 인덱스를 동시에(ALL) 재구성하면 잠금 시간과 리소스 경합이 최대화됩니다.
-
테이블의 인덱스 목록을 확인합니다.
SELECT name FROM sys.indexes WHERE object_id = OBJECT_ID('[테이블명]') AND index_id > 0; -
비클러스터드 인덱스부터 먼저 재구성합니다. 클러스터드 인덱스는 테이블 자체이므로 잠금 영향이 가장 큽니다.
ALTER INDEX [IX_비클러스터드인덱스명] ON [테이블명] REBUILD; -
마지막으로, 서비스 최소 사용 시간대를 확보하여 클러스터드 인덱스를 재구성합니다. 이와 같은 sQL Server 에이전트 작업을 이용해 특정 시간에 자동 실행되도록 스케줄링하는 것이 바람직합니다.
전문가 팁: NOLOCK 힌트의 함정 인덱스 재구성 중 발생하는 블로킹을 피하기 위해 애플리케이션 쿼리에
WITH (NOLOCK)힌트를 무분별하게 추가하는 것은 위험합니다. 이는 디버깅이 어려운 데이터 정합성 문제(더티 리드, 팬텀 리드)를 초래할 수 있습니다. 대신, 위에서 설명한 데이터베이스 수준의 해결책(온라인 재구성, 작업 분할)을 먼저 적용해야 합니다. 쿼리 최적화를 통해 인덱스 스캔 범위를 줄이는 것만으로도 잠금 경쟁을 상당히 완화시킬 수 있습니다.
주의사항: 작업 전 필수 체크리스트
인덱스 재구성은 시스템에 부하를 주는 작업입니다. 무계획적인 실행은 예상치 못한 장애로 이어질 수 있습니다. 반드시 다음 사항을 확인하고 실행하십시오.
-
백업 확인: 작업 전 반드시 데이터베이스 전체 백업을 수행합니다. 잘못된 작업으로 인한 물리적 손상에 대비하는 최후의 보험입니다.
-
용량 모니터링: 인덱스 재구성, 특히 온라인 방식은 기존 인덱스 공간의 두 배에 가까운 여유 공간과 트랜잭션 로그 공간을 필요로 합니다. 작업 전 디스크 사용률을 확인하고, 로그 파일의 자동 증가 설정이 적절한지 검토합니다.
-
세션 강제 종회 계획:
KILL명령어를 준비합니다. 만약 재구성 작업 자체가 시스템을 마비시키거나, 예상보다 너무 오래 걸릴 경우, 작업 세션을 강제로 종료해야 할 수 있습니다. 이 경우 작업은 롤백되며, 롤백에도 상당한 시간이 소요될 수 있음을 인지해야 합니다. -
변경 관리 절차 준수: 운영 환경에서는 반드시 테스트(Staging) 환경에서 동일한 작업을 먼저 수행하여 소요 시간과 리소스 영향도를 측정합니다. 이를 바탕으로 공식 유지보수 시간대를 계획하고, 관련 부서에 작업 공지를 합니다.
인덱스 재구성은 데이터베이스 성능 유지를 위한 필수 작업이지만, 동시에 주요 장애 원인이 될 수도 있습니다. 서비스 중단을 최소화하는 핵심은 “잠금의 범위와 시간을 관리”하는 것입니다. 에디션에 따른 온라인 옵션 사용이 최선의 방법이며, 불가능할 경우 철저한 계획 하에 작업을 작은 단위로 분할하여 위험을 분산시키는 전략이 현실적인 해결책이 됩니다. 항상 작업의 목표가 단편화 제거 그 자체가 아니라, ‘안정적인 서비스 제공’이라는 점을 명심하고 접근해야 합니다.