현재 문제점
크롤링을 진행하는 메서드 전체에 트랜잭션이 잡혀있다.
크롤링 해올 페이지가 적은 상황에선 문제가 안 되지만, 여러 페이지를 한 트랜잭션에 포함시킬 때 문제가 될 수 있다.
중간에 한 페이지에서 문제가 생기면 그동안 저장된 모든 데이터도 롤백되므로 결국 전체 크롤링 결과가 날아가게된다.
트랜잭션과 락
데이터베이스 시스템에서 트랜잭션과 락(Lock)은 데이터의 일관성과 무결성을 보장하기 위한 핵심 메커니즘이다.
데이터베이스 락은 여러 사용자나 프로세스가 동시에 같은 데이터에 접근할 때 발생할 수 있는 충돌을 방지하기 위한 메커니즘이다.
트랜잭션이 특정 데이터를 처리하는 동안, 그 데이터에 락을 걸어서 다른 트랜잭션의 접근을 제어함으로써 데이터의 일관성을 유지한다.
락은 크게 두 가지로 존재하는데,
공유 락(Shared Lock):
데이터를 읽을 때 사용된다.
여러 트랜잭션이 동시에 같은 데이터에 공유 락을 설정할 수 있어서 동시에 데이터를 읽는 것이 가능하다.
그러나 공유 락이 설정된 데이터는 변경할 수 없다. 보통 SELECT 문을 실행할 때 자동으로 설정된다
배타 락(Exclusive Lock):
데이터를 변경(삽입, 수정, 삭제)할 때 사용된다.
배타 락이 설정된 데이터는 다른 트랜잭션이 읽거나 수정할 수 없다.
한 번에 하나의 트랜잭션만이 특정 데이터에 배타 락을 설정할 수 있으며, 다른 트랜잭션은 그 락이 해제될 때까지 대기해야 한다.
INSERT, UPDATE, DELETE 같은 데이터 수정 명령어를 실행할 때 배타 락이 자동으로 설정된다.
데이터베이스 시스템은 다양한 수준에서 락을 설정할 수 있는데,
행 수준 락(Row-level lock): 테이블의 특정 행(레코드)에만 락을 설정
테이블 수준 락(Table-level lock): 테이블 전체에 락을 설정
데이터베이스 수준 락(Database-level lock): 데이터베이스 전체에 락을 설정
일반적으로 락의 범위가 작을수록(행 수준) 동시성 제어에서 유리하고,
범위가 클수록(테이블 수준) 동시성 제어가 까다로워진다.
락 에스컬레이션(Lock Escalation)
데이터베이스 시스템은 성능 최적화를 위해 락 에스컬레이션이라는 메커니즘을 사용한다.
이는 특정 객체에 너무 많은 락이 설정되면, 시스템이 자동으로 더 높은 수준의 락으로 전환하는 것을 의미한다.
(여러 개의 행 수준 락을 하나의 테이블 수준 락으로 변환한다거나)
긴 트랜잭션이 많은 수의 행에 락을 설정하면, 락 에스컬레이션이 발생할 가능성이 높아진다.
이 경우, 테이블 전체에 락이 설정되어 동시성이 크게 저하될 수 있다.
작은 범위로 접근하는 다른 트랜잭션들이 전부 락 해제를 기다려야 하고, 성능이 저하되고, 병렬성이 떨어진다.
트랜잭션의 길이와 성능 저하
트랜잭션의 길이는 트랜잭션이 시작되어 완료(커밋 또는 롤백)될 때까지의 시간을 의미한다.
트랜잭션이 길어질수록 락이 유지되는 시간도 길어지며, 이는 다양한 문제를 일으킨다.
1. 동시성 저하와 처리량 감소
긴 트랜잭션은 데이터에 오랜 시간 락을 유지한다.
이로 인해 다른 트랜잭션들은 필요한 데이터에 접근하지 못하고 대기 상태에 놓이게 된다.
이런 대기 상태가 많아질수록 시스템의 전체 처리량(throughput)은 감소하게 된다.
2. 시스템 리소스 낭비
대기 중인 트랜잭션들은 여전히 시스템 리소스를 점유한다.
너무 많은 트랜잭션이 대기 상태에 놓이면 시스템의 가용 리소스가 고갈되어 새로운 트랜잭션의 처리가 불가능해질 수 있다.
데이터베이스 커넥션 풀이 고갈되거나 서버의 메모리가 부족해지면 시스템 전체의 응답성이 떨어지게 된다.
심각한 경우 데이터베이스 서버가 다운되거나 애플리케이션 서버에 장애가 발생할 수도 있다.
트랜잭션의 길이가 길어질수록 긴 트랜잭션이 더 많은 자원을 더 오랜 시간 점유하기 때문에 데드락 발생확률도 높아진다.
데드락이란 두 개 이상의 트랜잭션이 서로가 보유한 자원을 기다리며 무한 대기하는 상태를 말한다.
데이터베이스 시스템은 일반적으로 데드락을 감지하면 한 트랜잭션을 강제로 롤백시켜 데드락을 해소하는데,
롤백된 트랜잭션은 재시도되어야 하므로 추가적인 시스템 부하가 발생한다.
트랜잭션 전파 속성 사용
forEach문 내부의 반복되는 코드를 메서드로 분리해서 트랜잭션 전파 속성 중 하나인 REQUIRES_NEW를 사용하는 방향으로 코드를 리팩토링했다.
각 페이지 마다 새로운 트랜잭션이 생성되도록 해서,
한 페이지에서 예외 발생 시 해당 페이지의 작업만 롤백되고 다른 페이지의 크롤링 작업은 정상적으로 진행되도록 하였다.
하지만 전체 작업의 원자성이 떨어지는 문제가 있기 때문에 그 점이 좀 걸리긴 한다.
https://www.sqlpassion.at/archive/2014/02/25/lock-escalations/
Lock Escalations – SQLpassion
www.sqlpassion.at
'팀 프로젝트 > cheerha.project' 카테고리의 다른 글
다중 인스턴스용 스케줄러(2) 각종 오류 해결과 리팩토링 (0) | 2025.03.09 |
---|---|
다중 인스턴스용 스케줄러(1) 스케줄러 중앙 제어 구현 (0) | 2025.03.07 |
크롤링 한 데이터를 어떻게 처리할까? (0) | 2025.03.05 |