트랜잭션
작업의 완전성, 즉 데이터의 정합성을 보장해 주는 기능.
작업의 일부만 적용되는 현상이 발생하지 않게 만들어주는 기능이다. all or nothing
프로그램 코드에서 트랜잭션의 범위를 최소화하는 것이 좋다.
네트워크 작업이 있는 경우 반드시 트랜잭션에서 배제해야 한다.
잠금
동시성을 제어하기 위한 기능.
잠금이 없다면 하나의 데이터를 여러 커넥션에서 동시에 변경할 수 있게 되어 레코드의 값을 예측할 수 없게 된다.
여러 커넥션의 요청을 순서대로 진행할 수 있게 해준다.
MySQL에서 사용되는 잠금은 스토리지 엔진 레벨과 MySQL 엔진 레벨로 나눌 수 있다.
MySQL 엔진 레벨의 잠금은 모든 스토리지 엔진 레벨에 영향을 미친다.
그 역은 영향을 미치지 않는다.
MySQL 엔진의 잠금
글로벌 락
락 획득 명령
FLUSH TABLES WITH READ LOCK
MySQL 서버의 모든 변경 작업을 멈추는 락으로 제공되는 잠금 중 가장 범위가 크다.
InnoDB 스토리지 엔진은 트랜잭션을 지원하기 때문에 일관된 데이터 상태를 위해 모든 변경 작업을 멈출 필요가 없다.
온라인 작업에 상당한 영향을 미치기에 애플리케이션에서 사용할 일은 거의 없다.
테이블 락
락 획득 명령
LOCK TABLES table_name [READ|WRITE]
명시적 획득 반납 명령
UNLOCK TABLES
온라인 작업에 상당한 영향을 미치기에 애플리케이션에서 사용할 일은 거의 없다.
묵시적 락
InnoDB는 스토리지 엔진에서 레코드 기반의 잠금을 제공한다.
그래서 대부분의 DML 쿼리에서는 무시되고, DDL 쿼리에만 영향을 미친다.
네임드 락
락 획득 명령
GET_LOCK() 함수
임의의 문자열에 대해 잠금을 설정할 수 있다.
메타데이터 락
테이블이나 뷰 같은 데이터베이스 객체의 이름이나 구조를 변경하는 경우 획득하는 잠금.
명시적인 획득/해제가 아닌 자동으로 획득되는 잠금.
InnoDB 스토리지 엔진 잠금
8.0 부터 기본 스토리지 엔진으로 채택되었으며 트랜잭션을 지원한다.
MySQL에서 제공하는 잠금과는 별개로 스토리지 엔진 내부에서 레코드 기반 잠금을 한다.
MyISAM보다 뛰어난 동시성 처리 제공.
레코드 락
레코드 자체를 잠근다는 뜻이지만 InnoDB는 레코드가 아닌 인덱스의 레코드를 잠근다.
인덱스가 없는 테이블이더라도 내부적으로 자동 생성된 클러스터 인덱스를 이용해 잠금한다.
레코드를 잠그느냐, 인덱스를 잠그느냐의 차이는 상당히 크고 중요하다.
갭 락
레코드 자체가 아닌 레코드와 바로 인접한 레코드 사이의 간격만의 잠금.
즉 레코드와 레코드 사이에 새로운 레코드가 생성되는 것을 제어한다.
갭 락은 주로 넥스트 키 락의 일부로 사용된다.
넥스트 키 락
레코드 락과 갭 락을 합쳐놓은 형태.
REPEATABLE READ
격리 수준을 사용해야 한다.넥스트 키 락과 갭 락은 데드락이나 트랜잭션 지연을 유발하곤 하는데, 바이너리 로그 포맷을 ROW로 바꿔 이 락을 줄이는 것이 좋다.
8.0 부터는 기본 설정 되었다.
자동 증가 락
테이블마다 유일한
AUTO_INCREMENT
속성을 지원하기 위한 테이블 수준의 잠금.INSERT, REPLACE와 같이 새로운 레코드를 저장하는 쿼리에서만 필요하다.
UPDATE, DELETE에서는 잠금되지 않음.
명시적 획득/해 방법은 없다.
다른 잠금과 달리 트랜잭션과 무관하게
AUTO_INCREMENT
값을 가져오는 순간만 잠금된다.한번 증가된
AUTO_INCREMENT
값은 INSERT가 실패해도 증가된 채로 유지된다.
자동 증가 락의 작동 방식을 제어하는 시스템 변수. innodb_autoinc_lock_mode
innodb_autoinc_lock_mode=0
MySQL 5.0 과 동일한 잠금 방식으로 모든 INSERT문은 자동 증가 락을 사용한다.
innodb_autoinc_lock_mode=1
연속 모드
Consecutive mode
라고 불뤼며, 예측 가능한 개수의 INSERT에 사용.빠른 속도를 위해 자동 증가 락이 아닌 래치를 사용.
여러 개의 자동 증가 값을 한번에 할당 받는다.
하나의 INSERT문의 레코드는
AUTO_INCREMENT
값의 연속성이 보장된다.발급된
AUTO_INCREMENT
값을 사용하지 않는 경우 누락된다.
innodb_autoinc_lock_mode=2
하나의 INSERT문의 레코드라도
AUTO_INCREMENT
값의 연속성을 보장하지 않는다.인터리빙 모드
Interleaved mode
라고 불린다.AUTO_INCREMENT
값의 유니크함을 보장한다.
인덱스와 잠금
InnoDB의 잠금은 레코드가 아닌 인덱스를 잠근다.
만약 UPDATE를 위한 인덱스가 없는 테이블이라면 풀 스캔하면서 테이블 전체 레코드를 잠그게 되어 동시성이 떨어진다.
격리 수준
하나의 트랜잭션, 또는 복수 개의 트랜잭션 간의 작업 내용을 어떻게 공유하고 차단할지 결정하는 레벨
READ UNCOMMITTED
서로 다른 트랜잭션에서의 쿼리 수행이 커밋이나 롤백 여부에 관계 없이 보인다. DIRTY READ
데이터의 정합성을 보장할 수 없어 일반적으로 사용되지 않는다.
READ COMMITTED
서로 다른 트랜잭션에서의 쿼리 수행은 커밋 된 것만 보인다.
변경된 데이터는 실제 테이블에 업데이트 되지만, 실제 커밋이 되기 전의 조회는 언두 영역의 데이터를 가져온다.
즉 커밋 되지 않은 데이터는 다른 트랜잭션에서 보이지 않는다.
오라클 DBMS의 기본 격리 레벨로 온라인 서비스에서 가장 많이 선택되는 레벨이다.
NON-REPEATABLE READ가 발생할 수 있다.
사용자 A 트랜잭션 시작 - BEGIN
사용자 B 트랜잭션 이름이 Toto인 회원을 조회한다. <- 결과 없음.
사용자 A 트랜잭션 id 500번의 회원 이름을 Toto로 변경한다. <- BEGIN, UPDATE, COMMIT
사용자 B 트랜잭션 이름이 Toto인 회원을 조회한다. <- 조회 성공.
위의 흐름에서 사용자 B 트랜잭션은 하나의 트랜잭션 내에서 서로 다른 조회 결과가 발생한다.
정합성이 중요한 업무에서 문제가 될 수 있다.
REPEATABLE READ
MySQL InnoDB의 기본 격리 레벨으로 여러 버전의 MVCC를 지원한다.
READ COMMITTED MVCC를 지원하지만 COMMIT되기 전까지의 언두 영역 레코드를 보여준다.
REPEATABLE READ 더 과거 버전의 레코드까지도 접근 가능하다.
트랜잭션이 길어질수록 관리해야되는 버전이 많아짐으로 언두 영역의 레코드가 많아진다.
하나의 트랜잭션에서 일관된 조회를 보장한다.
모든 트랜잭션은 순차적으로 증가하는 번호를 가진다.
자신의 트랜잭션 번호보다 작은 트랜잭션 번호에서 변경된 것만 보인다.
PHANTOM READ가 발생할 수 있다.
SELECT ... FOR UPDATE 문은 SELECT하는 레코드에 쓰기 잠금을 걸어야 한다.
하지만 언두 영역에는 잠금을 걸 수 없기에 현재 레코드의 값을 가져온다.
이 때 이전 SELECT에서는 조회되지 않던 레코드가 조회될 수 있다.
InnoDB 스토리지 엔진에서는 갭 락과 넥스트 키 락 덕분에 REPEATABLE READ 에서도 PHANTOM READ가 발생하지 않는다.
SERIALIZABLE
가장 단순하지만 가장 엄격하기에 동시 처리 성능이 떨어진다.
InnoDB 테이블에서 SELECT 문은 레코드 잠금이 필요하지 않지만, SERIALIZABLE 격리 레벨에서는 공유 잠금(읽기 잠금)을 획득해야 한다.
한 트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서는 접근할 수 없는 것이다.
Last updated