[DB] 동시성 제어

2023. 12. 20. 21:41CS/DataBase

자동차 전용도로에 시간차를 충분히 두고 차를 진입시키면 충돌 사고가 발생하지 않는다. 그러나 차를 제어하지 않고 임의로 진입시키면 충돌 사고가 발생할 수 있다. 트랜잭션도 마찬가지이다. 한 개의 트랜잭션이 끝나고 다음 트랜잭션을 수행시키면 데이터베이스의 일관성에 문제가 없다. 그러나 데이터베이스는 공유를 목적으로 하기 때문에 가능한 많은 트랜잭션을 동시에 수행시켜야 한다. 동시에 수행되는 트랜잭션은 다른 트랜잭션이 같은 데이터를 공유하고 있다는 사실을 모를 수 있기 때문에 일관성이 훼손될 수 있다. 트랜잭션이 동시에 수행될 때, 일관성을 해치지 않도록 트랜잭션의 데이터 접근을 제어하는 DBMS의 기능을 동시성 제어라고 한다.

 

두 개의 트랜잭션이 한 개의 데이터에 동시 접근할 때 발생할 수 있는 상황을 정리한 것이다. 같은 데이터에 접근하는 두 트랜잭션의 작업에 따라 세 가지 상황이 가능하다.

 

 

두 트랜잭션이 각각 읽기만 하는 경우 상관없다. 두 트랜잭션 중 하나가 쓰기 작업을 하는 경우로 자세한 내용은 3절에서 살펴본다. 두 트랜잭션이 모두 쓰기 작업을 하는 경우 갱신손실 문제가 발생할 수 있다. 여기에서 일어날 수 있는 문제와 그 해결책에 대해 알아보자.

 

갱신손실 문제

 

갱신손실 문제는 2개의 트랜잭션이 한 개의 데이터를 동시에 갱신할 때 발생한다. 갱신손실 문제는 데이터베이스에서 절대 발생하면 안 되는 현상이다.

 

작업 설명 : 하나의 데이터에 두 트랜잭션이 접근하여 갱신하는 작업

트랜잭션 T1, T2가 있다 T1은 예금을 인출하는 작업을 하고, T2는 입금하는 작업을 한다. T1은 계좌 X에서 100을 빼고 T2는 계좌 X에 100을 더한다. 초기의 X값이 1000이라면 T1 → T2 혹은 T2 → T1 어느 순서로 진행해도 결과는 X=1000이어야 된다. 이 경우 일관성 조건은 X 계좌의 총합이 1000이라는 것이다.

 

시나리오 : 두 개의 트랜잭션이 동시에 작업을 진행

트랜잭션 T1과 T2를 다음과 같이 수행하였다. 주의할 점은 데이터베이스에 저장된 데이터 값은 일단 주기억장치의 버퍼로 가져와야 읽을 수 있다는 점이다. 트랜잭션은 버퍼에 저장된 데이터로 작업을 진행한다. 나중에 버퍼의 내용을 데이터베이스에 기록하는 작업은 DBMS의 임무이다.

 

 

  • 문제 발생 : 갱신손실

두 트랜잭션이 종료된 후 X 값을 보면 1100으로 일관성이 깨진 것을 알 수 있다. 원인은 2에서 읽어 온 값이 T1의 트랜잭션이 끝나지 않은 상태값을 가져왔기 때문에 값이 갱신되지 않은 문제가 발생한 상태에서 잘못된 값을 덮어씨우면서 문제가 발생한 것이다.

이러한 갱신손실 문제를 해결하려면 T1 T2 혹은 T2   T1 식으로 순서대로 실행하면 되지만, 데이터베이스의 공유와 빠른 응답을 처리하기 위하여 그럴 수 없다. 

 

 

갱신손실 문제를 해결하려면 상대방 트랜잭션이 데이터를 사용하는지 여부를 알 수 있는 규칙이 필요하다. 즉 자신이 데이터를 수정 중이라는 사실을 알리면 된다. 알리는 방법으로는 락이라는 잠금장치를 사용한다.

 

락의 개념

락은 트랜잭션이 데이터를 읽거나 수정할 때 데이터에 표시하는 잠금 장치이다. 락을 이용하여 자신이 사용할 데이터를 잠그면 다른 트랜잭션은 잠금이 풀릴 때까지 기다려야 한다. 예를 들어 락을 사용하여 수정하면 다음과 같다. T1이 X에 락을 설정하였기 때문에 T2는 자신이 원하는 락을 얻지 못하고 대기한다. T2는 T1이 X의 락을 해지하면 진행할 수 있다. 락을 사용하면 데이터 X에 대한 갱신을 순차적으로 진행할 수 있기 때문에 갱신손실 문제를 해결할 수 있다.

 

 

 

락의 유형

락은 트랜잭션이 다루는 데이터를 다른 트랜잭션이 접근하지 못하도록 막아 대기 상태로 만든다. 다른 트랜잭션을 대기 상태로 만드는 일은 사용자의 응답시간에 영향을 주기 때문에 가능한 최소화 해야 한다. 수강신청을 할 때 동시 접속사 수가 많으면 응답시간이 느려져 불편을 느끼는데, 같은 이유라고 보면 된다.

 

트랜잭션이 다루는 데이터는 읽기만 하는 데이터, 읽고 쓰는 데이터, 쓰기만 하는 데이터가 있다. 이 중에서 읽기만 하는 데이터를 다루는 트랜잭션은 어느 정도 허용을 해도 문제가 없다. 이 점을 고려하여 락의 유형을 두 가지로 나눈다. 데이터 읽기를 표시하는 락과 데이터를 쓰기를 수정하는 락이다. 트랜잭션이 읽기를 할 때 사용하려는 락을 공유락이라 하고, 읽고/쓰기를 할 때 사용하는 락을 배타락이라고 한다.

 

2개의 락을 사용하는 규칙

  • 데이터에 락이 걸려있지 않으면 트랜잭션은 데이터에 락을 걸 수 있다.
  • 트랜잭션이 데이터 X를 읽기만 할 경우 LS(X)를 요청하고, 읽거나 쓰기를 할 경우 LX(X)를 요청한다.
  • 다른 트랜잭션이 데이터에 LS(X)를 걸어두면, LS(X)의 요청은 허용하고 LX(X)는 허용하지 않는다.
  • 다른 트랜잭션이 데이터에 LX(X)을 걸어두면, LS(X)와 LX(X) 모두 사용하지 않는다.
  • 트랜잭션이 락을 허용받지 못하면 대기 상태가 된다.

트랜잭션의 공유 락은 상호 허용이 되지만, 배타락은 허용되지 않는 것이 락 호환행렬의 핵심이다.

 

2단계 락킹

락을 사용하면 갱신손실 문제를 해결할 수 있다. 하지만 락을 걸고 해제하는 시점에 제한을 두지 않으면 두 개의 트랜잭션이 동시에 실행될 때 데이터의 일관성이 깨질 수 있다. 즉 데이터에 락을 걸었다 풀고 다시 거는 중간 과정에 락 해지 상태가 생기며 다른 트랜잭션에 노출될 수 있다. 이것을 방지하는 방법이 2단계 락킹이다.

 

  • 확장 단계 : 트랜잭션이 필요한 락을 획득하는 단계로, 이 단계에서는 이미 획득한 락을 해제하지 않는다.
  • 수축 단계 : 트랜잭션이 락을 해제하는 단계로, 이 단계에서는 새로운 락을 획득하지 않는다.

트랜잭션 T1, T2가 있다. T1은 예금이체 작업을 하고 T2는 이자계산 작업을 한다. 초기 A, B 두 데이터 값은 1000이다. 각 트랜잭션의 세부 작업을 보면 T1은 A 계좌에서 100을 인출하여 B 계좌에 입금하고, T2는 A, B 두 계좌의 잔고를 10%씩 증가시킨다. 작업 결과는 T1 → T2 혹은 T2 →  T1 어느 순서로 진행해도 A + B = 2200이 되어야 한다. 순서는 상관 없다. 

 

문제 발생 : 락을 사용하였지만 2단계 락킹 기법은 적용하지 않음 

트랜잭션 T1에서 작업을하다 중간에 락을 해제하였고 그사이 T2가 락을 걸고 데이터를 변형하였다. 때문에 원래 나와야 되는 2200이 아닌 2190이 나오게 되었다. 이상적인 방향은 T1 작업이 끝난 값 A=900, B=1100을 가져가 10% 증가시며 T2는 A=990, B=1210으로 만들어 도합 2200의 값을 저장해야 한다.

 

문제 해결 : 2단계 락킹 기법 사용

 

앞에서는 T1 작업중 중간에 트랜잭션이 해지되어 발생된 문제였다. 여기서는 2단계 락킹 기법을 이용해 T1 트랜잭션이 완료될 때까지 락을 걸어 두고 락이 해지되면 T2 트랜잭션이 실행되어 정상적인 값을 출력하게 된다.

확장단계에서는 락을 걸기만 하고 수축단계에서는 락을 해지만 하면된다.

 

 

데드락 

2단계 락킹 기법을 사용하면 데이터의 일관성을 유지할 수 있다. 그렇지만 두 개 이상의 트랜잭션이 각각 자신의 데이터에 대하여 락을 획득하고 상대방 데이터에 대하여 락을 요청하면 무한 대기 상태에 빠질 수 있다. 이러한 현상을 데드락 혹은 교착상태라고 한다.

 

작업 설명 : 두 개의 데이터에 두 개의 트랜잭션이 접근하여 갱신하는 작업

마당서점 데이터를 접근하는 트랜잭션 T1, T2가 있다. T1은 도서번호가 1인 도서와 도서번호가 2인 도서의 가격을 100원 올리고, T2는 역순으로 도서번호가 2인 도서와 도서번호가 1인 도서의 가격을 10% 인상한다.

 

문제 발생 : 데드락 발생

T1은 bookid가 1인 도서에 락을 걸고, T2는 bookid가 2인 도서에 락을 건다. 그리고 T1이 bookid가 2인 도서에 락을 요청하면 대기상태가 된다. 이 순간 T2도 bookid가 1인 도서에 락을 요청하면 대기 상태가 된다. T1과 T2는 서로 상대방이 락을 해제하기를 기다리는 상태가 된다.

 

문제 해결 : 데드락 해결

일반적으로 데드락이 발생하면 DBMS는 T1 혹은 T2의 작업 중 하나를 강제로 중지시킨다. 그 결과 나머지 트랜잭션은 정상적으로 실행된다. 이때 중지시키는 트랜잭션에서 변경한 데이터는 원래 상태로 되돌려 놓는다. 앞의 예에서 T2를 중지시킨다면 T1이 변경한 도서번호 1의 가격은 원래 상태로 바꾸어 놓아야 한다.

 

이전에 진행했던 프로젝트에서 댓글을 작성하는 코드에서 데드락이 발생한 적이 있는데 클라이언트에서 중복 전송으로 인한 문제였는지 하나의 pk에 중복 데이터를 2개를 넣는 과정에서 대기상태가 발생하여 데드락이 발생한 경험이 있었다. 이때는 DBMS에서는 롤백을 클라이언트에서는 중복 전송 방지 코드를 추가해 해결했었다.

 

 

'CS > DataBase' 카테고리의 다른 글

[DB] 회복  (0) 2023.12.21
[DB] 트랜잭션 고립 수준  (0) 2023.12.21
[DB] 트랜잭션  (0) 2023.12.20
[DB] 정규화  (0) 2023.12.19
[DB] 데이터 모델링  (0) 2023.12.18