여러 트랜잭션이 동시에 같은 데이터에 접근하거나 변경하려고 할 때 발생하는 충돌 현상
이런 상황을 동시성 문제(Concurrency Problem)라고 하며, 이를 적절히 제어하지 않으면 다음과 같은 문제가 발생할 수 있다.
트랜잭션을 서로 격리(Isolation) 해서 실행하면,
하나의 트랜잭션이 처리 중인 작업이 다른 트랜잭션에 영향을 주지 않도록 만들 수 있다.
격리 수준은 트랜잭션이 다른 트랜잭션에서 수행한 작업을 얼마나 허용할지를 정의한다.
예를 들어, 한 트랜잭션이 아직 커밋하지 않은 데이터를
다른 트랜잭션이 읽을 수 있게 할지, 아니면 완전히 막을지를 설정할 수 있다.
격리 수준 조정이 필요한 이유
가장 간단한 해결책은 트랜잭션을 순차적으로 하나씩 처리하는 것이다.
하지만 이렇게 하면 병렬로 실행되는 트랜잭션이 없기 때문에
시스템 성능이 급격히 떨어질 수 있다.그래서 실제 DBMS는 성능과 정합성을 모두 고려해,
다양한 격리 수준을 제공하며,SET TRANSACTION ISOLATION LEVEL문이나
Spring에서의@Transactional(isolation = Isolation.REPEATABLE_READ)처럼 트랜잭션에 격리 수준을 명시할 수 있다.
격리 수준을 구현하기 위해 주로 Lock(잠금)을 사용한다.
읽기 작업은 쓰기 작업과 충돌할 수 있고, Lock이 길게 유지되면 성능 병목이 발생할 수 있다.
예를 들어, 두 사용자가 동시에 데이터를 수정하려고 할 때
모두에게 락을 걸면 데이터 정합성은 유지되지만 성능은 하락한다.
즉, 무조건 락을 거는 방식은 현실적인 해결책이 아니다.
락을 너무 많이 걸면 성능이 떨어진다.
락을 적게 걸면 데이터 정합성이 무너질 수 있다.
그래서 DBMS는 격리 수준을
Read Committed / Repeatable Read / Serializable 등으로 세분화하여,
트랜잭션 간 간섭을 최소화하면서도 성능을 확보하고 있다.
select 가능 여부에 따른 Lock의 종류와 특징
- 공유 락(shared lock)
데이터를 읽을 때 사용되는 락으로, 공유 락끼리 동시에 접근이 가능하다.- 배타 락(exclusive lock)
데이터를 변경할 때 사용되는 락으로, 트랜잭션이 완료될 때까지 유지되며 해당 락이 해제되기 전까지 다른 트랜잭션이 접근할 수 없다.
| 수준 | Dirty Read | Non-Repeatable Read | Phantom Read | 설명 |
|---|---|---|---|---|
| Read Uncommitted | 가능 | 가능 | 가능 | 사실상 사용하지 않음 |
| Read Committed | 불가능 | 가능 | 가능 | 대부분의 RDBMS 기본 설정 (ex. Oracle) |
| Repeatable Read | 불가능 | 불가능 | 가능 | InnoDB 기본값, 같은 행은 일관된 값 보장 |
| Serializable | 불가능 | 불가능 | 불가능 | 가장 엄격, 트랜잭션을 순차적으로 실행 |
동시성 문제 유형에 따른 격리 수준
문제 유형 설명 방지 방법 Dirty Read A 트랜잭션이 아직 커밋되지 않은 B 트랜잭션의 데이터를 읽음 Read Committed 이상 Non-Repeatable Read A가 같은 데이터를 두 번 읽는데 중간에 B가 수정을 해서 값이 바뀜 Repeatable Read 이상 Phantom Read A가 조건에 맞는 데이터를 조회했는데, B가 중간에 데이터를 추가함 Serializable Lost Update 두 트랜잭션이 동시에 같은 데이터를 수정하여 한 쪽의 결과가 덮어씌워짐 Optimistic Lock or Pessimistic Lock Non-Repeatable Read vs. Phantom Read
구분 설명 발생 원인 Non-Repeatable Read 같은 row를 두 번 읽었을 때 값이 달라지는 현상UPDATEPhantom Read 같은 조건으로 범위 조회를 두 번 했는데 결과가 달라지는 현상INSERT,DELETE
현실에서는 대부분의 관계형 데이터베이스 시스템(RDBMS)이 기본적으로 Read Committed를 격리 수준으로 사용한다.
이 수준은 Dirty Read를 방지하면서도 성능 부담이 적어, 일반적인 업무 시스템에서 안정적인 선택지가 된다.
격리 수준이 낮을수록 트랜잭션 간 충돌 가능성은 증가하지만, 처리량(Throughput)은 높아진다.
따라서 성능이 중요한 서비스에서는 낮은 격리 수준을 설정하되, 다음과 같은 보완 전략을 함께 사용한다.
결제, 송금, 재고 관리처럼 데이터 정합성이 매우 중요한 도메인에서는 다음과 같은 방법으로 무결성을 보장한다.
이처럼 처리 성능보다 데이터 신뢰성이 중요한 경우, 높은 격리 수준을 적용하거나 비즈니스 로직 수준에서 정합성을 강화한다.