지난 주 Transaction Isolation level에 대해 학습하던 중 격리 수준에 따른 lock여부, 기본적인 lock 개념에 대해 부족한 부분이 많아 이를 학습하고자 한다.
트랜잭션의 특징 중 일관성이 보장될 경우 여러 클라이언트의 요청을 받는 DB 특성 상 응답 지연 현상이 발생해 동시성이 저해될 수 있어 이 둘은 trade-off 관계에 있다.
그렇다고, 일관성을 높인다면 트랜잭션들이 줄을 서서 차례대로 접근하는 문제가 발생해 효율이 떨어지게 되므로, 일관성과 동시성을 적절한 균형설정을 해줄 필요가 있다.
- 낙관적 동시성 제어
동일 데이터를 동시에 수정하지 않음. 데이터를 읽는 시점에 락을 걸진 않지만 수정하는 시점에 기존에 읽어온 데이터가 다른 사용자에 의해 변경되었는지 검토 필요- 비관적 동시성 제어
동일 데이터를 동시에 수정. 데이터를 읽는 시점에 락을 걸어 변경 시까지 락을 유지
: MySQL 엔진, 스토리지 엔진 레벨로 나눌 수 있다.
- MySQL 엔진
: 모든 스토리지 엔진에 영향을 미침. MySQL 사용하며 처리되는 모든 DB에 적용
- 글로벌 락 : 모든 테이블에 락
- 테이블 락 : 각 테이블 단위로 락, DDL 같은 테이블의 스키마에 변화 줄 때 사용
- 네임 락 : 테이블, 뷰 등 스키마 객체 이름 변경 시 자동으로 락
- 유저 락 : 사용자가 지정한 문자열에 락
- 스토리지 엔진
스로리지 엔진 간 영향 X, 전체가 아닌 해당 영역에만 LOCK 적용
비관적 락 : 트랜잭션에서 변경하려는 레코드에 대해 락 획득 후 쿼리 수행
낙관적 락 : 일단 쿼리 수행 후 충돌 있었나 확인하고 문제 있으면 충돌 있던 트랙잭션 롤백
- 레코드 락 : 레코드가 아닌 인덱스를 잠금 (기본키, 유니크 키에 의한 변경 작업)
- 갭 락 : 인덱스와 인접한 앞 뒤 공간에 락을 거는 것 (넥스트 키 락에서 사용)
- 넥스트 키 락 : 레코드 락 + 갭 락 (인덱스, 인덱스의 앞 뒤 모두 락)
해당 락 사용 시 의도치 않게 넓은 범위에 락을 걸게 될 수 있음.- 오토 인크리먼트 락 : MySQL에서 자동 증가하는 숫자 값 채번 시 컬럼 AUTO 적용하는데, 이때 같이 Insert 요청시 사용되는 lock
ex) A 트랜잭션이 USER라는 테이블에서 사용자 NAME이 SON인 경우를 SELECT하는 하고 B 트랜잭션이 NAME을 KANE으로 UPDATE 후 COMMIT했다. 이후 A트랜잭션이 이름이 SON인 사람의 이름을 SUN으로 바꾸고자 하는 경우라고 가정해보자.
이 상황에서는 최종 결과로 NAME은 KANE이 되고 B트랜잭션에서 이름을 바꾼 후 COMMIT했으므로, UNDO영역에 기존 이름 SON의 내용이 있어야 A트랜잭션이 일관성을 보장받을 수 있다.
이후 A트랜잭션에서 UPDATE를 진행할 때 베타 lock이 필요한데 A트랜잭션이 바라보는 영역은 UNDO이지만, UNDO 영역에 대해서는 베타 lock을 걸 수 없다.
그러므로 UPDATE 구문에서 베타 락을 시도하지만, name이 son인 레코드가 존재하지 않으므로 아무 일도 일어나지 않게 되어 A 트랜잭션의 실행으로 인한 UPDATE결과가 반영되지 않고 두 트랜잭션 사이에 데이터 부정합이 발생하여 Phantom read 문제가 발생한다.