본문 바로가기

Web Sever 개발과 CS 기초/DB 지식

DB Lock에 대한 이해와 MySQL Lock의 특징

개요 목적

이번 시간에는 DB Lock에 대해서 알아본다.

먼저 DB 락의 필요성과 역할을 알아본 후에 Lock종류와 Lock에서 발생할 수 있는 문제도 알아본다.

그리고 Lock 문제 중 데드락을 해결하는 방법을 제시한다.

마지막으로, 많이 쓰이는 MySQL은 어떻게 Lock을 사용하고 있는지 알아보자.

DB Lock의 필요성과 역할

DB Lcok이란, 한 세션에서 트랜잭션을 시작하고 데이터를 수정하는 동안 커밋이나 롤백되기 전에는 다른 세션이 해당 데이터를 수정할 수 없게 하는 역할을 한다. 그렇지 않으면, 데이터의 무결성을 보장할 수 없다.

Lock 개념과 헷갈리는 트랜잭션의 개념과 글에서 자주 나오는 세션 개념을 아래 블로그 글에서 확인할 수 있다.

[백엔드/데이터베이스] - Transaction과 ACID 쉽게 이해하기(+MySQL transaction 설정 방법)

DB Lcok을 사용하지 않아서 데이터의 무결성이 보장되지 않는 예를 통해서 DB Lock의 필요성을 알아보자.

(상황: member_a의 돈에 세션1은 +500을 하게 되고, 세션2는 -1000을 하려고 한다. 그렇다면 결과적으로 money가 5500이 되어야 한다.)

그러나 Lock 없다면,

  • 세션1이 데이터에 +500을 해서 6500을 만들었다. 아직 커밋하지 않았다.
  • 세션2가 세션 1의 결과가 반영되지 않은 데이터(6000)에 접근해서 5000을 만들었다. 아직 커밋하지 않았다.
  • 세션1이 커밋을 한다. 6500 money가 되었다.
  • 세션2도 커밋을 한다. 5000 money가 되었다.

(세션1이 수정하는 동안 세션2가 데이터에 접근하여, 결과가 5500이 아닌, 5000이 되는 문제가 발생했다.)

Lock이 있다면,

  • 세션1이 데이터에 락을 흭득하고 데이터에 +500을 해서 6500을 만들었다. 아직 커밋하지 않았다.
  • 세션2가 데이터에 접근을 하려고 하는데, 세션1이 데이터에 대한 Lock을 가지고 있어서 접근하지 못한다. 대기한다.
  • 세션1이 커밋을 한다. 6500 money가 되었다.
  • 세션2가 락이 반납된 데이터에 접근해서 5500을 만들고 커밋한다. 결과가 올바르게 5500이 되었다.

(DB Lock을 사용하여 세션1이 결과를 반영하기 전에 세션2가 접근할 수 없도록 막아, 올바른 데이터 결과를 만들어 냈다.)

Lock의 종류

공유(shared) Lock

공유락은 데이터를 읽을 때 사용하는 Lock이다. 그래서 Read Lock이라고 불린다. 공유락이 설정된 데이터에 다른 공유락의 접근이 가능하다. 즉, 하나의 데이터를 여러 사람이 동시에 읽는 것은 가능하다. 그러나 공유 락이 설정된 데이터에는 배타락(쓰기락)은 접근할 수 없다.

배타(Exclusive)락

배타락은 데이터를 변경할 때 사용하는 Lock이다. 그래서 Write Lock이라고 불린다. 배타락이 설정된 데이터에 관해서는 다른 공유락 배타락을 설정할 수 없다. 배타락은 해당 트랜잭션이 완료될 때까지 유지 된다.

Lock에서 발생할 수 있는 문제

1. 블로킹(blocking)

블로킹은 Lock간의 경합(공유 - 배타, 배타 - 배타)이 발생 했을 때, 트랜잭션이 멈춰선 상태를 의미한다. 공유 락끼리는 블로킹이 발생하지 않지만, 배타락이 경합에 껴있으면, 블로킹이 발생한다. 예시를 통해 블로킹 발생을 알아보자.

트랜잭션A가 먼저 데이터 YKH에 업데이트를 하며 배타락을 걸었다. 그 뒤에 트랜잭션 B가 YKH 데이터를 읽어서, 배타락과 공유락이 경합하게 되었다. 그래서 트랜잭션B의 작동이 멈춰선 Blocking 상태가 발생했다. Blocking 상태를 해결하려면 먼저 시작된 트랜잭션A가 완료(Commit)되어야 한다.

2. 교착상태(DeadLock)

데드락은 두 트랜잭션이 각자 다른 데이터에 배타락을 설정해둔 후에, 서로 상대방의 데이터에 락을 설정하려고 할 때 발생한다. 두 트랜잭션 모두 동작이 멈추었기 때문에, 두 트랜잭션 영원히 멈춘 상태를 교착 상태 데드락이라고 한다. 예시를 통해서 데드락 발생을 알아보자.

트랜잭션 A는 game_master 2번 데이터에 배타락을 걸었다. 그 때 트랜잭션 B는 gmae_detail 2번 데이터에 배타락을 설정했다. 그 이후 트랜잭션 A는 game_detail 2번 데이터에 접근하기 위해 락을 걸어서 멈춰버렸고, 트랜잭셔 B는 game_master 2번 데이터에 접근하기 위해 락을 걸어서 멈춰버렸다. 두 트랜잭션 모두 완료(Commit)할 수 없어서, 두 트랜잭션이 영원히 멈춘 교착 상태에 빠지데 된다.(블로킹 에서는 다른 먼저 시작된 트랜잭션이 완료되면, 블로킹이 해제된다.)

교착상태 발생을 줄이려면, 데이터 접근 순서를 세우는 것이 중요하다. 위 예제라면, game_master를 업데이트 한 후 game_detail을 업데이트 한다와 같은 규칙을 정해 테이블 접근의 교차가 일어나지 않도록 하는 것이다.

2-1. 교착 상태 해결 방법

  1. timeout 설정하기. 일정 시간 동안 트랜잭션이 실행되지 않는 경우 롤백 처리한다.
  2. 데이터 접근 순서를 세우기. 위 예제라면, game_master를 업데이트 한 후 game_detail을 업데이트 한다와 같은 규칙을 정해 테이블 접근의 교차가 일어나지 않도록 한다.
  3. wait - die 또는 wound - wait 방법. 타임 스탬프를 사용해서, 트랜잭션 생성 시기로 선점여부를 결정하는 방식이다. (TIMESTAMP는 '1970-01-01 00:00:01' UTC부터 시작하는 초 수를 나타낸다. 예를 들어, T200이 T50보다 최신의 시간이다)
    • wait - die 방식: 트랜잭션 Ti가 Tj의 로킹된 데이터를 요청하면 Ti가 먼저 들어온 트랜잭션이라면 기다린다(wait). Ti가 나중에 들어온 트랜잭션이라면 포기(die)하고 나중에 다시 요청한다.
    • woud - wait 방식: 트랜잭션 Ti가 Tj의 로킹된 데이터를 요청하면 Ti가 먼저 들어온 트랜잭션이라면 선점(wound)한다. Ti가 나중에 들어온 트랜잭션이라면 기다린다. (wait)

MySql의 Lock 특징

MySql에서는 공유락과 배타락을 사용한다. 또한 Lock 레벨로는 행 단위의 레코드 락을 사용한다.

Record Lock

primary key 또는 unique index (multi-column unique index 포함)로 조회해서 하나의 인덱스 레코드(=row=행)에만 lock을 거는 것을 의미한다.

예) SELECT id FROM t WHERE id = 10 LOCK IN SHARE MODE

  • 위 쿼리를 실행하면 id=10 인 레코드에 대해 공유락이 걸린다.

예) SELECT id FROM t WHERE id = 10 FOR UPDATE

  • 위 쿼리를 실행하면 id=10 인 레코드에 대해 배타락이 걸린다.

MySql Repeatable Read 격리 수준과 Lock

MySql의 격리 수준은 REPEATABLE READ 레벨이다. REPEATABLE READ 레벨에서는 한 트랜잭션안에 여러 업데이트 가 발생하면, 해당 트랜잭션이 완료 되었을 때, 업데이트에 사용한 여러 배타락을 한번에 해제한다.

한 단계 낮은 READ COMMITTED 레벨에서는 트랜잭션 내에 한 row가 update 되고 다음 row로 넘어갈 때 update된 row의 배타락을 풀어준다.

MySql 데드락 처리

MysSql의 엔진인 InnoDB는 데드락 감지 기능을 가지고 있다. 그래서 데드락이 감지되면, 2개의 트랜잭션 중 데이터 변화가 적은 것을 rollback시켜서 데드락을 해소한다. 하지만, 데드락 감지 기능은 속도 저하의 원인이 될 수 있다. 그래서 MySql은 innodb_lock_wait_timeout을 사용하는 것을 권장한다.innodb_lock_wait_timeout은 row lock을 걸고 특정 시간 이상 걸리면 자동으로 lock을 해제 하는 매커니즘이다.

멘토의 코칭

→MySql은 다양한 엔진을 사용한다. 그래서 이것은 MySql의 특징 보다는 InnoDB엔진의 특징이다. MySql이 사용하는 데이터 엔진에 따라서 제공하는 기능이 달라진다. 구식 엔진의 경우 트랜잭션과 락을 지원하지 않을 수 있다.


Reference

https://sabarada.tistory.com/121

https://www.letmecompile.com/mysql-innodb-lock-deadlock/

https://sabarada.tistory.com/122?category=822063