[MySQL] 트랜잭션과 락
데이터베이스에서 트랜잭션은 하나 이상의 SQL 그룹으로, 그룹 단위로 실행이 성공하거나 실패함을 보장한다.
트랜잭션의 주요 속성으로는 ACID가 있다.
Atomicity : 트랜잭션 단위 작업은 모두 완료되거나 모두 실패한다.
Consistency : 데이터베이스를 일관성 있는 상태로만 다뤄야 한다. (계좌이체에서 인출됐지만 입금되지 않은 상태가 없어야 함)
Isolation : 동시에 실행되는 트랜잭션들끼리는 서로 영향을 주지 않는다
Durability : 트랜잭션이 커밋된 후 해당 내용은 영구적으로 보존되어야 한다.
트랜잭션이 길어질수록 리소스를 많이 사용하게 되니 최소한의 코드에 트랜잭션을 적용하자.
특히 네트워크 작업이 있는 경우 반드시 트랜잭션에서 배제하자.
락 (Lock) 은 데이터베이스에서 동시성을 관리해 일관성과 무결성을 유지하기 위해 사용되는 매커니즘이다.
락이 없다면 하나의 데이터를 여러 사용자가 동시에 변경할 수 있게 된다.
글로벌 락
MySQL 서버 전체에 영향을 미치는 락으로, MySQL에서 제공하는 잠금 중 가장 범위가 크다.
MyISAM이 mysqldump로 백업 받을 때 사용한다.
FLUSH TABLES WITH READ LOCK 명령어로 락을 획득할 수 있고, 획득 시 새로운 연결들은 읽기 작업만 가능해진다.
테이블 락
개별 테이블 단위로 설정되는 잠금으로 읽기 락과 쓰기 락으로 구분된다.
MyISAM 엔진에서 주로 사용되고, 쿼리가 실행되는 동안 자동으로 획득됐다가 쿼리가 완료된 후 자동으로 해제된다.
InnoDB 엔진에서는 레코드 기반의 락을 제공한다. 테이블 락이 설정되긴 하지만 DML에서는 무시되고 DDL에서만 영향을 준다.
네임드 락
GET_LOCK() 함수로 임의의 문자열에 대해 잠금을 설정한다.
여러 인스턴스가 동시에 같은 작업을 수행함을 방지할 때 사용한다.
임의의 문자열에 대한 잠금을 가지도록 해서 특정 시간에 실행되는 배치 작업이 한 번만 수행됨을 보장할 때 사용할 수 있다.
메타데이터 락
데이터베이스 객체의 이름이나 구조를 변경할 때 획득하는 잠금이다.
명시적으로 획득하거나 해제할 수 없고, 객체를 변경할 때 자동으로 획득한다.
한 세션에서 테이블 구조를 변경할 때 다른 세션에서 해당 테이블에 접근하는 작업을 막을 때 사용한다.
InnoDB 엔진의 락
InnoDB 스토리지 엔진은 레코드 기반 잠금을 제공해 동시성 처리에 유리하다.
information_scheme 데이터베이스의 INNODB_TRX, INNODB_LOCKS 등을 사용해 어떤 트랜잭션이 잠금을 가지는지 확인할 수 있다.
레코드 락 : 말 그대로 레코드 자체만 잠그는 락으로, 인덱스의 레코드에 락을 건다. (MySQL에서 인덱스 설계가 중요한 이유이다..)
갭 락 : 레코드와 바로 인접한 레코드 사이의 간격만을 잠그는 락이다. 레코드와 레코드 사이에 새로운 레코드가 생성됨을 방지한다.
넥스트 키 락 : 레코드 락과 갭 락을 합친 형태이다.
자동 증가 락 : 트랜잭션이 자동 증가 값을 삽입할 때 획득하는 락으로 AUTO INCREMENT의 고유값을 보장하기 위해 사용된다.
트랜잭션의 격리 수준은 다수의 트랜잭션이 동시에 처리될 때 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있도록 설정하는 수준이다.
MySQL에서는 네 가지 격리수준을 지원한다.
1. READ UNCOMMITTED
각 트랜잭션에서의 변경 내용이 어떻게 되든 다른 트랜잭션에서 보이는 수준으로, 정합성에 문제가 많은 격리 수준이다.
트랜잭션이 커밋되지 않았는데도 내용을 볼 수 있어 롤백될 시 더티 리드 현상이 발생한다.
2. READ COMMITTED
다른 트랜잭션에서 커밋한 내용만 볼 수 있는 격리 수준으로, 오라클에서는 기본값으로 사용된다.
한 트랜잭션에서 UPDATE 한 데이터를 커밋되기 전에 다른 트랜잭션에서 조회할 시 해당 트랜잭션에서는 Undo Log에 백업된 데이터를 조회하게 된다.
더티 리드 문제는 발생하지 않지만, 한 트랜잭션 내부에서 같은 데이터를 여러 번 읽을 때 매번 같은 결과를 보장할 수 없어 NON-REPEATABLE READ 문제가 발생할 수 있다.
3. REPEATABLE READ
트랜잭션이 시작될 때의 데이터 상태를 계속해서 유지하는 격리 수준으로, InnoDB 스토리지 엔진에서 기본으로 사용된다.
MVCC를 통해 Undo Log에 백업된 데이터를 보여줄 수 있어 동일 트랜잭션 내부에서는 같은 결과를 보장할 수 있다.
모든 InnoDB의 트랜잭션은 고유한 번호가 있고, Undo Log은 트랜잭션 번호로 이력을 관리한다.
NON-REPEATABLE READ 문제는 발생하지 않지만, Phantom Read 현상은 발생할 수 있다.
언뜻 보면 MVCC와 Undo Log덕분에 InnoDB에서는 팬텀 리드 현상이 발생하지 않는 것 처럼 보이는데...
일반적으로는 변경하거나 삭제할 때는 Undo Log에 기록할 수 있지만 새로 추가되는 데이터는 Undo Log에 기록할 수 없어 팬텀리드가 발생할 수 있다.
하지만 InnoDB의 넥스트 키 락 덕분에 팬텀 리드 현상을 방지할 수 있다.
그럼에도.. SELECT FOR UPDATE 또는 SELECT FOR SHARE 쿼리를 사용할 때는 Undo Log에 락을 걸 수 없어 팬텀 리드 현상이 발생할 수 있다.
4. SERIALIZABLE
가장 엄격한 격리 수준으로, 팬텀리드 / 더티리드 / NON-REPEATABLE READ 모두 발생하지 않는다.
한 트랜잭션이 읽고 쓰는 레코드를 다른 트랜잭션에서 절대 접근할 수 없는 격리 수준이다.
'Database > MySQL' 카테고리의 다른 글
[MySQL] 인덱스 (0) | 2023.08.30 |
---|---|
[MySQL] 아키텍처 (0) | 2023.08.25 |