본문 바로가기
IT/AWR-ASH

락(Lock) 관련 대기 분석: 테이블 락, 행(Row) 락 등 트랜잭션 대기로 인한 문제 해결

by free-inf 2025. 2. 9.

서버 이관 프로젝트를 수행하는 과정에서 데이터베이스의 성능과 안정성을 유지하는 것은 가장 중요한 과제 중 하나다. 새로운 환경에서 기존과 다른 성능 저하가 발생할 가능성이 있으며, 특히 트랜잭션이 증가하면서 락(Lock) 대기 시간이 길어지는 문제가 발생할 수 있다.

 

락(Lock)은 데이터베이스에서 동시에 실행되는 여러 트랜잭션 간의 데이터 무결성을 보장하기 위해 사용되는 메커니즘이다. 그러나 락이 과도하게 발생하면 다른 트랜잭션이 대기 상태로 유지되면서 성능 저하와 응답 지연을 초래할 수 있다.

 

본 글에서는 락 대기 이벤트의 개념과 주요 원인, SQL을 활용한 락 대기 분석 방법, 그리고 성능 최적화를 위한 해결 방안을 설명한다. 이를 통해 서버 이관 후 발생할 수 있는 락 대기 문제를 효과적으로 진단하고 해결할 수 있도록 한다.


1. 락(Lock) 대기 이벤트란?

1.1 락(Lock)의 개념

락(Lock)은 여러 트랜잭션이 동일한 데이터에 동시 접근할 때, 데이터의 일관성과 무결성을 보장하기 위해 사용되는 동기화 기법이다.

  • 공유 락(Shared Lock, S-Lock): 여러 트랜잭션이 데이터를 읽을 수 있지만, 쓰기는 제한됨.
  • 배타 락(Exclusive Lock, X-Lock): 하나의 트랜잭션만 데이터에 쓰기를 수행할 수 있으며, 다른 트랜잭션은 읽기도 불가능.

락이 적절하게 관리되지 않으면, 트랜잭션이 서로 기다리는 상태가 지속되면서 성능 저하로 이어질 수 있다.


1.2 락 관련 대기 이벤트의 종류

Oracle 데이터베이스에서 발생하는 주요 락 대기 이벤트는 다음과 같다.

대기 이벤트설명원인

enq: TX - row lock contention 행(Row) 수준에서 락이 발생하여 트랜잭션이 대기하는 현상 동일한 행을 여러 트랜잭션이 동시에 수정하려고 할 때
enq: TM - contention 테이블 수준에서 락이 발생하여 트랜잭션이 대기하는 현상 테이블에 대해 DML(INSERT, UPDATE, DELETE) 작업이 많을 때
enq: UL - contention 사용자 정의 락(User Lock)으로 인해 대기 발생 애플리케이션에서 수동으로 설정한 락이 해제되지 않은 경우
library cache lock SQL 실행 계획 캐시에서 충돌이 발생하는 현상 여러 세션이 동일한 SQL을 동시에 변경하는 경우

락이 과도하게 발생하면 트랜잭션 처리가 지연되면서 전체 시스템 성능에 악영향을 미칠 수 있다.


2. 락(Lock) 대기 분석 방법

락 대기 이벤트를 분석하려면 ASH(Active Session History) 데이터와 V$LOCK 뷰를 활용하는 것이 효과적이다.

2.1 ASH 데이터를 활용한 락 대기 분석

아래 SQL을 실행하면 특정 시간 동안 발생한 락 대기 이벤트를 확인할 수 있다.

 
SELECT event, COUNT(*) AS wait_count
FROM v$active_session_history
WHERE wait_class = 'Concurrency'
AND sample_time BETWEEN TO_DATE('2024-02-07 10:00:00', 'YYYY-MM-DD HH24:MI:SS')
AND TO_DATE('2024-02-07 10:30:00', 'YYYY-MM-DD HH24:MI:SS')
GROUP BY event ORDER BY wait_count DESC;
  • wait_class = 'Concurrency': 락과 관련된 대기 이벤트만 필터링.
  • COUNT(*) AS wait_count: 가장 빈번하게 발생한 락 대기 이벤트를 확인.

이 데이터를 활용하면, 트랜잭션 경합이 심한 테이블이나 SQL을 파악할 수 있다.


2.2 V$LOCK을 활용한 락 상태 분석

현재 데이터베이스에서 어떤 세션이 락을 보유하고 있으며, 어떤 세션이 대기 중인지 확인하려면 다음 SQL을 실행한다.

 
SELECT l.sid, s.serial#, s.username, l.type, l.mode_held, l.mode_requested
FROM v$lock l JOIN v$session s ON l.sid = s.sid
WHERE l.block = 1;
  • l.sid: 락을 보유한 세션의 ID.
  • s.username: 해당 세션의 사용자.
  • l.mode_held / mode_requested: 현재 보유 중인 락과 요청한 락의 유형.
  • l.block = 1: 현재 다른 트랜잭션을 블로킹하는 락만 조회.

이 데이터를 활용하면, 어떤 세션이 락을 유지하면서 다른 세션을 대기시키고 있는지를 파악할 수 있다.


2.3 블로킹 세션 감지

특정 세션이 다른 세션을 차단하고 있는 경우, 블로킹 세션을 감지하여 해결할 수 있다.

 
SELECT blocking_session, sid, serial#, event, wait_time
FROM v$session
WHERE blocking_session IS NOT NULL;
  • blocking_session: 다른 세션을 차단하는 블로킹 세션 ID.
  • sid, serial#: 대기 중인 세션의 정보.
  • event: 대기하고 있는 이벤트.

이 데이터를 활용하면, 어떤 트랜잭션이 시스템을 정체시키고 있는지 분석하고 필요한 조치를 취할 수 있다.


3. 락(Lock) 대기 문제 해결 방법

3.1 트랜잭션 크기 조정 및 커밋(Commit) 최적화

긴 트랜잭션이 락을 오랫동안 유지하는 경우, 트랜잭션을 작은 단위로 나누고, 커밋을 적절한 시점에 수행하는 것이 중요하다.

 
-- 비효율적인 SQL (트랜잭션이 너무 길게 유지됨)
BEGIN
 
UPDATE employees
SET salary = salary * 1.1
WHERE department = 'Sales'; -- 많은 데이터가 한 번에 수정됨
 
COMMIT;
 
END;
-- 최적화된 SQL (트랜잭션 크기를 조정)
BEGIN
  FOR emp IN (SELECT emp_id FROM employees WHERE department = 'Sales') LOOP
        UPDATE employees SET salary = salary * 1.1 WHERE emp_id = emp.emp_id;
        COMMIT; -- 개별적으로 커밋하여 락 유지 시간 단축
  END LOOP;
END;

3.2 적절한 인덱스 활용

인덱스가 적절하게 설정되지 않으면 테이블 락이 과도하게 발생할 수 있다. 인덱스를 활용하면 락이 행(Row) 수준에서 발생하도록 유도하여 트랜잭션 충돌을 줄일 수 있다.

-- 인덱스가 없는 경우 (테이블 전체가 락에 걸릴 가능성 높음)
UPDATE employees SET salary = salary * 1.1 WHERE department = 'Sales';
 
-- 적절한 인덱스 추가 (락 범위 최소화)
CREATE INDEX emp_dept_idx ON employees(department);

 


3.3 블로킹 세션 강제 종료(Kill Session)

블로킹 세션이 장시간 유지되는 경우, 관리자가 세션을 강제 종료할 수 있다.

ALTER SYSTEM KILL SESSION '1234,56789';
  • 1234,56789: 종료할 세션의 SID와 SERIAL#.

 

락(Lock) 대기 이벤트는 트랜잭션 처리 속도를 저하시킬 수 있는 주요 원인 중 하나이며, 이를 적절하게 분석하고 해결하지 않으면 전체 데이터베이스 성능이 저하될 가능성이 크다.

 

ASH 및 V$LOCK 뷰를 활용하여 락을 분석하고, 트랜잭션 크기 조정, 인덱스 최적화, 블로킹 세션 관리 등의 기법을 적용하면 락 대기로 인한 성능 문제를 효과적으로 해결할 수 있다.