개요 목적
이번 시간에는 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. 교착 상태 해결 방법
- timeout 설정하기. 일정 시간 동안 트랜잭션이 실행되지 않는 경우 롤백 처리한다.
- 데이터 접근 순서를 세우기. 위 예제라면, game_master를 업데이트 한 후 game_detail을 업데이트 한다와 같은 규칙을 정해 테이블 접근의 교차가 일어나지 않도록 한다.
- 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
'Web Sever 개발과 CS 기초 > DB 지식' 카테고리의 다른 글
MySQL 데이터 타입 이해와 Java와 Type Mapping(+MSSQL) (0) | 2022.11.12 |
---|---|
Read 시도 후 없으면 Insert , 있으면 Update 쿼리를 만들면 안 되는 이유 +Upsert 쿼리 알아보기 (0) | 2022.10.23 |
정규화(1,2,3,BNCF) 쉽게 이해하기 (0) | 2022.10.01 |
DB Isolation Level 격리 수준 이해하기 (0) | 2022.10.01 |
데이터베이스 구성 요소와 키(PK, FK)에 대한 이해 (0) | 2022.10.01 |