병행제어(Concurrency Control)

최석훈·2025년 10월 20일

데이터베이스

목록 보기
12/18

병행제어(Concurrency Control)란

병행제어란 여러개의 트랜잭션이 실행될 때 트랜잭션들이 데이터베이스의 일관성을 파괴하지 않고 다른 트랜잭션에 영향을 주지 않으면서 트랜잭션을 제어하는 것을 의미합니다.
병행제어의 목적은 데이터 베이스의 공유와 시스템 활용도의 최대화, 데이터 베이스의 일관성 유지, 사용자에 대한 응답시간을 최소화하는데 있습니다.

트랜잭션이 동시에 실행되면 ?

Dirty Write

같은 데이터에 대해 동시에 두 개 이상의 트랜잭션이 값을 바꾸고자 할 때 발생되는 현상

Dirty Read

아직 종료(commit)되지 않은 트랜잭션의 쓰기 내용을 읽는 것으로 비정상적 상태의 데이터를 읽게 되는 현상

Non-repeatable Read

어떤 트랜잭션에서 동일한 데이터의 값을 매번 읽을 때 마다 틀려지는 현상

Phantom Read

기존 데이터는 동일한 데 새로 추가된 값에 의해 데이터 값이 변경되는 현상

병행제어를 하지 않으면 ?

1. 분실된 갱신

두 개의 트랜잭션이 같은 데이터에 대해서 동시에 갱신 작업을 하면 하나의 갱신 작업이 분실되는 경우

2. 모순성

한 개의 트랜잭션 작업이 갱신 작업을 하고 있는 상태에서 또 하나의 트랜잭션이 같은 작업 구역에 침범하여 작업하게 되어 데이터베이스의 일관성을 해치는 경우

3. 연쇄복귀

같은 자원을 사용하는 두개의 트랜잭션 중 한 개의 트랜잭션이 성공적으로 일을 수행하였다 하더라도 다른 트랜잭션이 처리하는 과정에서 실패하게 되면 두 개의 트랜잭션 모두가 복귀되는 현상

4. 비완료 의존성

한 개의 트랜잭션이 수행과정에서 실패하였을 때, 이 트랜잭션이 회복되기 전에 다른 트랜잭션이 수행 결과를 참조하는 현상

MySQL의 4가지 Isolation Level

Isolation Level설명방지되는 문제
READ UNCOMMITTED가장 낮은 수준의 격리. 커밋되지 않은 데이터를 읽을 수 있어 Dirty Read 발생 가능❌ Dirty Read 발생
READ COMMITTED커밋된 데이터만 읽을 수 있음. Dirty Read 방지 가능✅ Dirty Read 방지
REPEATABLE READ (기본값)한 트랜잭션 내에서 같은 데이터를 반복해서 읽을 때 항상 같은 결과를 보장. Non-repeatable Read 방지✅ Dirty Read, Non-repeatable Read 방지
SERIALIZABLE가장 높은 수준의 격리. 모든 트랜잭션이 직렬화되어 실행됨. Phantom Read까지 방지, 하지만 성능 저하✅ 모든 문제 방지 (성능 저하 있음)

병행제어 기법

1. 로킹(Locking)

트랜잭션이 어떤 데이터에 접근하고자 할 때 로킹을 수행하며 로킹을 한 트랜잭션만이 로킹을 해제할 수 있음. 로킹되어 있는 데이터에는 다른 트랜잭션이 접근할 수 없음, 트랜잭션은 로킹이 된 데이터에 대해서만 연산을 수행할 수 있으며 로킹의 단위에는 필드, 레코드, 파일, 데이터베이스 모두 로킹이 될 수 있음

  • 로킹 단위가 크면 : 관리하기가 용이(로킹 오버헤드 감소), 하지만 동시성 수준이 낮아짐

  • 로킹 단위가 작으면 : 동시성 수준이 높아지지만 관리가 까다로움(로킹 오버헤드 증가)

2단계 로킹 규약(Two-Phase Locking Protocol)

Lock과 Unlock이 동시에 이루어지면 일관성이 보장되지 않으므로 Lock만 가능한 단계와 Unlock만 가능한 단계를 구분하며 직렬가능성을 보장함, 하지만 교착상태가 발생할 수 있음.

  • 확장단계 : 트랜잭션이 Lock만 할 수 있고 Unlock은 할 수 없음

  • 축소단계 : 트랜잭션이 Unlock만 할 수 있고 Lock은 할 수 없음

2. 타임스탬프(Time Stamp)

데이터에 접근하는 시간을 미리 정하여 정해진 시간의 순서대로 데이터에 접근하며 수행함, 직렬가능성을 보장하며 시간을 나눠 사용하기 때문에교착상태가 발생하지 않음, 하지만 연쇄복귀를 초래할 수 있음.

3. 낙관적 병행제어(Optimistic Concurrency Control)

트랜잭션 수행 동안은 어떠한 검사도 하지 않고, 트랜잭션 종료 시에 일괄적으로 검사함, 트랜잭션 수행 동안 그 트랜잭션을 위해 유지되는 데이터 항복의 지역사본에 대해서만 갱신하며 트랜잭션 종료 시에 동시성을 위한 트랜잭션 직렬화가 검증되면 일시에 DB로 반영함

4. 다중 버전 병행제어(Multi-version Concurrency Control)

여러 트랜잭션이 동시에 같은 데이터를 읽고 쓰는 것을 허용하면서, 데이터 충돌을 최소화하는 병행제어 방법입니다.

읽기 작업은 잠금(Lock)을 걸지 않음
이전 버전(스냅샷)을 읽음
쓰기 작업은 충돌 시 롤백 처리

  • InnoDB의 MVCC 구현 방식

    Undo Log를 통해 데이터의 이전 버전을 보관

    트랜잭션은 시점(스냅샷) 기준으로 데이터를 읽음

    변경된 데이터는 Undo Log에 기록되며, 다른 트랜잭션은 이 로그를 참조해 이전 버전 조회

  • 동작 예시

    1.트랜잭션 A가 UPDATE를 수행 → 변경 전 데이터는 Undo Log에 저장됨

    2.트랜잭션 B는 동일 데이터를 읽으려 할 때 Undo Log에 저장된 이전 버전을 읽음

    3.이로 인해 읽기 작업에는 Lock이 필요 없음

    4.커밋되지 않은 데이터는 다른 트랜잭션이 볼 수 없음

EX)

-- Dirty Read 예시 (Isolation Level: READ UNCOMMITTED)
-- 트랜잭션 1
START TRANSACTION;
UPDATE accounts SET balance = 5000 WHERE id = 1;
-- 아직 COMMIT 안 함

-- 트랜잭션 2 (다른 세션)
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1;  -- 5000 읽음 (아직 커밋 안 된 값)

-- 트랜잭션 1이 ROLLBACK 하면?
-- 트랜잭션 2는 존재하지 않는 값을 읽은 것임

-- Lock 거는 방법
SELECT * FROM users WHERE id = 1 FOR UPDATE;  -- 배타적 Lock
SELECT * FROM users WHERE id = 1 FOR SHARE;   -- 공유 Lock

예시: Dirty Read

-- 트랜잭션 1
START TRANSACTION;
UPDATE accounts SET balance = 5000 WHERE id = 1;
-- COMMIT 안 함

-- 트랜잭션 2 (다른 세션)
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1;  -- 5000 읽음 (커밋 전 데이터)

Lock 예시

-- 배타적 Lock (다른 트랜잭션은 접근 불가)
SELECT * FROM users WHERE id = 1 FOR UPDATE;

-- 공유 Lock (읽기는 허용, 쓰기는 불가)
SELECT * FROM users WHERE id = 1 FOR SHARE;
profile
기술 노트 블로그 (2025.09.01~)

2개의 댓글

comment-user-thumbnail
2025년 10월 21일

개념 설명은 되어 있는데 실제로 어떻게 동작하는지 코드 예시가 전혀 없음.
특히 MySQL Isolation Level 개념이 완전히 빠져있는데 이게 병행제어의 핵심임.

-- Dirty Read 예시 (Isolation Level: READ UNCOMMITTED)
-- 트랜잭션 1
START TRANSACTION;
UPDATE accounts SET balance = 5000 WHERE id = 1;
-- 아직 COMMIT 안 함

-- 트랜잭션 2 (다른 세션)
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1;  -- 5000 읽음 (아직 커밋 안 된 값)

-- 트랜잭션 1이 ROLLBACK 하면?
-- 트랜잭션 2는 존재하지 않는 값을 읽은 것임

-- Lock 거는 방법
SELECT * FROM users WHERE id = 1 FOR UPDATE;  -- 배타적 Lock
SELECT * FROM users WHERE id = 1 FOR SHARE;   -- 공유 Lock

이런 식으로 직접 실행해봐야 동시성 문제가 어떻게 발생하는지 체감됨.

MySQL의 4가지 Isolation Level 설명 추가 필수임.

  • READ UNCOMMITTED: Dirty Read 가능
  • READ COMMITTED: Dirty Read 방지
  • REPEATABLE READ (MySQL 기본값): Non-repeatable Read 방지
  • SERIALIZABLE: 완벽한 격리, 성능 저하

MVCC도 추상적임. MySQL InnoDB는 undo log로 버전 관리한다는 것,
읽기는 Lock 안 걸고 이전 버전 읽는다는 구체적인 동작 방식 추가 필요함.

1개의 답글