[면접 대비] 데이터베이스 - 트랜잭션, 동시성

Swim Lee·2021년 4월 26일
1

기술면접 대비

목록 보기
1/13

데이터베이스

  • 트랜잭션 특징
  • 무결성 (개체 무결성, 참조 무결성)
  • 격리수준

트랜잭션과 격리수준

트랜잭션

ACID 특성 가지고 있다.

  • Atomicity (원자성) : 트랜잭션 내에서 실행한 작업들은 마치 하나의 작업인 것처럼 모두 성공하든가 모두 실패해야한다. (All or Nothing)
  • Consistency (일관성) : 모든 트랜잭션들은 일관성있는 데이터베이스 상태를 유지해야한다. 예를 들어 데이터베이스에서 정한 무결성 제약 조건을 만족해야한다. (개체 무결성, 참조 무결성)
  • Isolation (격리성) : 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리한다. 예를들어 동시에 같은 데이터를 수정하지 못하도록 해야한다. -> 격리성은 동시성과 관련된 성능이슈로 인해 격리수준을 선택할 수 있다.
  • Durability (영속성) : 트랜잭션을 성공적으로 끝내면 그 결과가 항상 기록되어야한다. 중간에 시스템에 문제가 발생하더라도 데이터베이스 로그등을 사용해서 성공한 트랜잭션 내용을 복구해야한다.

트랜잭션은 원자성, 일관성, 지속성을 보장한다. 문제는 격리성이다.
트랜잭션간에 격리성을 완벽히 보장하려면 트랜잭션을 거의 차례대로 실행해야한다. 이렇게 하면 동시성 처리 성능이 매우 나빠진다.
이런 이유로 ANSI 표준은 트랜잭션의 격리 수준을 4단계로 나누어 정의했다.

격리수준

  • READ UNCOMMITED (커밋되지 않은 읽기)
  • READ COMMITTED (커밋된 읽기)
  • REPEATABLE READ (반복가능한 읽기)
  • SERIALIZABLE (직렬화 가능)

순서대로 READ UNCOMMITED의 격리수준이 가장 낮고, SERIALIZABLE의 격리수준이 가장 높다

격리수준이 낮을 수록 동시성은 증가하지만, 격리수준에 따른 다양한 문제가 발생한다.

발생하는 문제의 종류는 다음 3가지가 있다.

  • DIRTY READ
  • NON-REPEATABLE READ
  • PHANTOM READ

격리수준이 낮을수록 더 많은 문제가 발생한다.

격리수준DIRTY READNON-REPEATABLE READPHANTOM READ
READ UNCOMMITEDOOO
READ COMMITEDOO
REPEATABLE READO
SERIALIZABLE

격리수준에 따른 문제점

READ UNCOMMITED

커밋하지 않은 데이터를 읽을 수 있다. 예를 들어서 트랜잭션1이 데이터를 수정하고 있는데 이를 커밋하지 않아도 트랜잭션2가 수정중인 데이터를 조회할 수 있다. 이것을 DIRTY READ라고 한다.
트랜잭션2가 DIRTY READ를 사용하는데 트랜잭션1이 롤백하면, 데이터의 정합성에 심각한 문제가 발생할 수 있다.
DIRTY READ를 허용하는 격리수준을 READ UNCOMMITED라고 한다.

READ COMMITTED

커밋한 데이터만 읽을 수 있다. 따라서 DIRTY READ가 발생하지 않는다.
하지만 NON-REPEATABLE READ가 발생할 수 있다.
예를들어 트랜잭션 1이 회원A를 조회중인데, 갑자기 트랜잭션2가 회원A를 수정하고 커밋하면 트랜잭션1이 다시 회원A를 조회했을 때, 수정된 데이터가 조회된다. 이처럼 반복해서 같은 데이터를 읽을 수 없는 상태를 NON-REPEATABLE READ라고 한다.
DIRTY READ는 허용하지 않지만, NON-REPEATABLE READ는 허용하는 격리수준을 READ COMMITED라고 한다.

REPEATABLE READ

한 번 조회한 데이터를 반복해서 조회해도 같은 데이터가 조회된다. 하지만 PHANTOM READ는 발생할 수 있다.
예를들어 트랜잭션1이 10살이하의 회원을 조회했는데 트랜잭션2가 5살 회원을 하나 추가하고 커밋하면 트랜잭션1이 다시 10살 이하의 회원을 조회했을 때 회원하나가 추가된 상태로 조회된다.
이처럼 반복 조회시 결과집합이 달라지는 것을 PHANTOM READ라고 한다.
NON-REPEATABLE READ는 허용하지 않지만, PHANTOM READ는 허용하는 격리수준을 REPEATABLE READ라고 한다.

SERIALIZABLE

가장 엄격한 트랜잭션 격리 수준이다. 여기서는 PHANTOM READ가 발생하지 않지만 동시성 처리 성능이 급격히 떨어질 수 있다.

애플리케이션은 보통 동시성 처리가 중요하므로 데이터베이스들은 보통 READ COMMITTED 격리수준을 기본으로 사용한다. 일부 중요한 비즈니스 로직에 더 높은 격리수준이 필요하면 데이터베이스 트랜잭션이 제공하는 잠금기능을 사용하면 된다.

참고링크 :
https://feco.tistory.com/45
https://private-space.tistory.com/97 (설명 그림 good)
https://nesoy.github.io/articles/2019-05/Database-Transaction-isolation (위의 링크 먼저 본 후 보기)

데이터베이스 동시성 제어 방법

Lock

데이터베이스 연산(read/write)을 수행하기 전에 해당 데이터에 먼저 lock 연산을 실행하여 독점권을 획득하는 방식으로 트랜잭션의 직렬가능성을 보장하는 방식
병행 수행되는 트랜잭션들이 동일한 데이터에 동시에 접근하지 못하도록 lock과 unlock이라는 2개의 연산을 이용해 제어.
기본 원리는 먼저 접근한 데이터에 대한 연산을 다 마칠 때까지, 해당 데이터에 다른 트랜잭션이 접근하지 못하도록 상호배제하여 직렬가능성을 보장하는 것.

  • 공유 락 (Shared Lock)
  • 배타적 락 (Exclusive Lock)

공유락

데이터를 읽을 때 사용되는 lock
공유락 끼리는 동시에 접근 가능. 즉, 하나의 데이터를 읽는 것은 여러 사용자가 (트랜잭션이) 동시에 할 수 있다.
하지만 공유락이 설정된 데이터에 배타락을 사용할 수는 없다.

배타락

배타락은 데이터를 변경하고자 할 때 사용됨.
트랜잭션이 완료될 때까지 유지된다.
배타락은 lock이 해제될 때까지 다른 트랜잭션(읽기 포함)은 해당 리소스에 접근할 수 없다. 또한 해당 lock은 다른 트랜잭션이 수행되고 있는 데이터에 대해서는 접근하여 함께 lock을 설정할 수 없다.

lock의 설정 범위

  • 데이터베이스 : 전체 데이터베이스에 락, DB에 하나의 세션만 접근 가능 (잘 안쓰임)
  • 파일 : DB 파일 기준으로 락. 파일은 DB row등과 같은 실제 데이터가 쓰여지는 물리적 저장소 (잘 안쓰임)
  • 테이블 : DB 테이블 수준의 락, 모든 행을 업데이트 하는 등의 전체 테이블에 영향을 주는 변경 수행시 유용. DDL구문과 함께 사용되며 DDL lock이라고도 함.
  • 페이지와 블럭 : 파일의 일부인 페이지와 블록을 기준으로 lock 설정. (잘 안쓰임)
  • 컬럼 : 일반적으로 사용안함. 지원하는 DBMS도 잘 없음
  • 로우 : DML에 대한 락으로, 가장 일반적으로 사용하는 lock

로킹 단위가 클수록 제어는 쉽지만 병렬성이 떨어지고
로킹 단위가 작을 수록 제어는 어렵지만 병렬성은 높아진다.

블로킹(Blocking)

블로킹은 lock간에 (배타-배타, 배타-공유)의 경합이 발생해서 특정 트랜잭션이 작업을 진행하지 못하고 멈춰선 상태를 말한다.
공유락끼리는 블로킹이 발생하지 않지만, 배타락은 블로킹을 발생시킨다.
블로킹을 해소하기 위해선, 이전의 트랜잭션이 완료 (커밋 or 롤백)되어야 한다.
뒤에 들어온 트랜잭션은 이전 트랜잭션이 마무리되어야 이후 진행이 가능.
이런 경합은 성능에 좋지않은 영향을 미치기 때문에 경합을 최소화할 필요가있다.

위 그림에서 트랜잭션 A가 로우를 업데이트하므로 2번 로우에 배타락을 걸었다. 트랜잭션 B는 2번 데이터를 읽어와야하는데, 해당 로우에 배타락이 걸려있으므로, 해당 락을 건 트랜잭션이 락을해제하고 종료되는 것을 기다린다. 이것이 바로 블로킹이다.

교착 상태(DeadLock)

두 트랜잭션이 각각 락을 설정한 다음 서로의 락에 접근하여 값을 얻어오려고 할 때, 이미 각각의 트랜잭션에 의해 락이 설정되어있기 때문에 양쪽 트랜잭션 모두 영원히 처리되지 않게 되는 상태를 말함.

위와 같은 그림이 바로 교착상태를 나타낸다.
트랜잭션 A는 game-master의 2번 로우를 업데이트 한 후 game-detail 테이블의 2번 로우를 수정한다. 트랜잭션 B는 반대로 game-detail의 2번 로우를 수정한 후, game-master의 2번 로우를 수정한다.
각각의 트랜잭션들이 하나의 로우에 배타적 락을 가지고 있고, 상대가 가진 락이 해제되기를 기다리고 있으므로 이 상태가 영원히 지속되게 되는데 이를 교착상태라한다.
따라서 교착상태가 발생하면 DBMS가 둘 중 한 트랜잭션에 에러를 발생시킴으로써 문제를 해결한다. 교착상태가 발생할 가능성을 줄이기 위해서는 접근 순서를 동일하게 하는 것이 중요! 위의 예시에서는 game-master를 먼저 접근한 후, game-detail을 접근한다 같은 규칙을 정해 테이블 접근의 교차가 일어나지 않도록 하는 것이 중요하다.

출처 : https://sabarada.tistory.com/121

profile
백엔드 꿈나무 🐥

0개의 댓글