초오오오오오짜개발자의낙서장
DB LOCK 과 트랜잭션 종류(진행중) 본문
트랜잭션 격리 수준
이해하기 위해서 먼저 lock을 알아야한다
크게 스토리지 엔진 레벨과 mysql 엔진 레벨로 나눌수 있다.
- 스토리지 엔진 레벨의 잠금 : 테이블의 데이터를 다루기 위한 락
- mysql 엔진 레벨의 잠금 : 테이블이나 데이터베이스 등과 같은 부분을 위한 락
스토리지 엔진 레벨의 잠금
- 레코드 락
- 갭 락
- 넥스트 키 락
- 자동 증가 락
레코드 락
- 테이블 레코드 자체를 잠그는 락
- 레코드 수준은 작은공잔으로 관리
- 레코드 락이 페이지 락 or 테이블 락으로 레벨업 되는 경우는 없다.
- mysql에서의 레코드 락은 테이블의 레코드가 아닌 인덱스의 레코드를 잠근다는 점에서 차이가 있다.
- mysql에서 인덱스와 테이블은 별도의 자료 구조로 관리되는데 인덱스에 락이 걸린다.
- 락이 걸린 인덱스는 클러스터 인덱스 및 논 클러스터 인덱스 모두를 포함한다.
- pk가 없는 테이블이라면 내부적으로 자동 생성된 pk를 이용하여 설정한다.
- 인덱스 레코드에 락을 거는 것과 테이블 레코드에 라글 거는 것에는 큰 차이가 있다.
- 예를 들어 성이 J로 시작하는 구성원이 300 명 있고 이름이 유니크한 사람 한명이 있다고 가정하자
- 해당 구성원 한명을 업데이트 하기위해 300건의 인덱스 레코드에 잠금이 걸린다. 이유는 mysql은 테이블 레코드가 아닌 인덱스에 잠금을 걸기 때문이다.
- 인덱스는 성으로만 구성되어 있기 때문에 해당 레코드를 갱신하기위해서는 인덱스를 통해 검색되는 모든 레코드에 잠금을 걸게 된다.
- 만약 적당한 인덱스가 없다면 모든 테이블의 레코드에 락을 걸고 테이블을 풀스캔 하면서 작업을 처리하게 되낟.
그러면 동시성이 상당히 떨어지게 되므로 특히 MySQL 에서 인덱스의 설계는 중요하다.
- 레코드 락은 트랙잭션이 DML 구문을 실행할 때 자동으로 거는 락,
- 레코드 락 덕분에 여러 트랜잭션이 동시에 서로 다른 레코드에 접근할 수 있다.
갭락
- 레코드가 아닌 레코드와 레코드 사이의 간격을 잠금으로써 레코드의 생성 ㅁ수정 및 삭제를 제어한다.
- j로 시작하는 레코드가 Jo Joe 2개가있다고 할때 얼마든지 다른 데이터 Jang, Jeong이 추가될 수 있다.
- 따라서 현재 트랜잭션에서 임의의 데이터가 추가되지 않도록 잠그려면 아래와 같은 쿼리를 실행해야 한다.
- select for update 구문은 베타적 잠금(비관적 잠금, 쓰기 잠금)을 거는 것이다.
- 읽기 잠금을 걸라면 LOCK IN SHARE MODE 구문을 사용해야한다.
- 락은 트랜잭션이 커밋 또는 롤백 될 떄 해제된다.
넥스트 키 락
- 레코드 락과 갭락을 합친 잠금
- 갭락은 단독으로 사용되기 보다 넥스트 키 락의 일부로 함께 사용된다.
- 갭락이나 넥스트 키 락은 바이너리 로그에 기록되는 쿼리가 리플리카 서버에서 실행될 때 소스 서버에서 만들어낸 결과와 동일한 결과를 만들어 내도록 보장해주는 것이 주목적이다.
- 하지만 넥스트 키 락과 갭 락으로 인해 데드락이 발생하거나 다른 트랜잭션이 기다리는 일이 자주 발생하므로 바이너리 로그 포맷을 ROW 형태로 바꿔서 넥스트 키 락이나 갭 락을 줄이는 것이 좋다고 한다.
자동 증가 락
- mysql은 자동 증가하는 숫자값을 채번하기 위해 AUTO INCREMENT라는 컬럼 속성을 제공하면 이는 주로 대체 키에 사용된다.
- AUTO_INCREMENT 컬럼은 여러 레코드가 동시에 INSERT 되더라도 중복되지 않고 순차적으로 증가하는 일련번호를 제공하기 위해 내부적으로 테이블 수준의 잠금인 자동 증가 락을 사용한다.
- 해당 락은 INSERT와 REPLACE와 같이 새로운 레코드를 저장하는 쿼리에만 사용된다.
- 트랜잭션과 관계없이 INSERT나 REPLACE문장에서 AUTO_INCREMENT 값을 가져오는 순간에 락이 걸린다.
- 자동 증가 락은 잠금을 최소화 하기위해 한 번 증가하면 절대 자동으로 줄어들지 않는다.
- 이는 트랜잭션과 무관하다.
트랜잭션의 격리 수준
- 여러 트랜잭션이 동시에 처리될때 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있게 허용할지 여부를 결정하는 것이다.
- 격리 수준이 높은 순서대로 SERIALIZABLE, REPEATABLE READ, READ COMMITTED, READ UNCOMMITED가 있다.
SEARIALIZABLE
- 가장 엄격한 격리 수준으로 이름 그대로 트랜잭션을 순차적으로 진행시킨다.
- 여러 트랜잭션이 동일한 레코드에 동시 접근할 수 없으므로 어떠한 데이터 부정합 문제도 발생하지 않는다.
- 트랜잭션이 순차적으로 처리되어야 하므로 동시 처리 성능이 매우 떨어진다.
- 원래 select 구문에서는 lock이 걸리지 않지만 SERIALIZABLE 격리 수준에서는 순수한 select 작업에서도 대상 레코드에 넥스트 키 락을 읽기 잠금으로 건다.
- 따라서 한 트랜 잭션에서 넥스트 키 락이 걸린 레코드를 다른 트랜젝선에서는 절대 추가 수정 삭제 할 수없다.
- 가장 안전하지만 가장 성능이 떨어지므로 극단적으로 안전한 작업이 필요한 경우가 아니면 사용해서는 안된다.
REPEATABLE READ
- 일반적인 RDBMSsms 변졍 전의 레코드를 언두 공간에 백업해둔다.
- 변경 전/후 데이터가 모두 존재하므로 동일한 레코드에 대해 여러 버전의 데이터가 존재한다고 하여 이를 MVCC(multi version Concurrency Control)라고 부른다
- MVCC를 통해 트랜잭션이 롤백된 경우에 데이터를 복원할 수 있을 뿐만 아니라 서로 다른 트랜잭션 간에 접근할 수 있는 데이터를 세밀하게 제어할 수 있다.
- 각각의 트래낵션은 순차 증가하는 고유한 트랜잭션 번호가 존재하면 백업 레코드에서는 어느 트랜잭션에 의해 백업 되었는지 트랜잭션 번호를 함께 저장한다.
- 해당 데이터가 불 필요해 진다고 판단하는 시점에 주기적으로 백그라운드 쓰레드를 통해 삭제한다.
- REPEATABLE READ는 MVCC를 이용해 한 트랜잭션 내에서 동일한 결과를 보장하지만 새로운 레코드가 추가되는 경우에 부정합이 생길 수 잇다.
-A 트랜잭션이 시작되고 조회하는중에 A가 종료되지 않은 상태에서 B 트랜잭션이 시작되어 데이터를 변경할때 MVCC를 통해 기존 데이터는 변경 되지만 백업된 데이터가 언두 로그에 남게된다.
- B 트랜젝션이 아직 종료되지 않은 상황에서 조회를 하면 어떻게 될까?
- REPEATED READ는 트랜잭션 번호를 참고하여 자신보다 먼저 실행된 트랜잭션의 데이터만을 조회한다.
- 만약 테이블에 자신보다 이후에 실핸된 트랜잭션의 데이터가 존재한다면 언두 로그를 참고해서 데이터를 조회한다.
- 따라서 나중에 실핸된 트랜잭션이 커밋까지 되었다지만 조회 결과로 기존과 동일한 데이터를 얻게 된다.
- 어떤 트랜잭션이 읽은 데이터를 다른 트랜잭션이 수정하더라도 동일한 결과를 반환 할 것을 보장해 준다.
- 새로운 레코드의 추가 까지는 ㅏㅁㄱ지 않는다.
- select로 조회한 경우 트랜잭션이 끝나기 전에 다른 트랜잭션에 의해 추가된 레코드가 발견될 수있는데 이를 유령읽기라고 한다.
- MVCC 덕분에 일반적인 경우에서는 panthom read가 발생하지 않는다.
- 자신보다 나중에 실행된 트랜잭션이 추가한 레코드는 무시하면 되기 때문이다.
- 잠금이 사용되는 경우 유령 읽기가 발생핤수 있다.
- mysql 은 다른 rdbms와 다르게 특수한 갭 락이 존재하기 때문에 일반적인 RDBMS 부터 살펴본다.
- 사용자 B가 먼저 데이터를 조회하는데 select for update를 이용하여 쓰기 잠금을 걸었다.
- 이떄 select for update는 베타적 잠금을 거는것이다.
- 락은
-
postgreSQL의 동시성 제어 방식 Lock MVCC