728x90
트랜잭션과 락
- 트랜잭션
- 작업의 완전성을 보장
- 데이터의 정합성 보장
- 작업의 완전성을 보장
- 잠금
- 동시성 제어 기능
트랜잭션
여러 논리작업이 한꺼번에(다같이 되거나 다같이 안되거나) 진행되도록 하는것이다.
그리고 트랜잭션은 무조건 모든곳에 넣는것은 좋지 않은데 예를 들어 유저가 글을 쓰는 로직을 볼 때
- 유저 정보 가져옴
- 글쓰기 오류 판별
- 업로드 파일 확인 및 저장
- 사용자 입력 정보 DBMS 저장
- 저장 내용을 DBMS에서 조회
- 게시물 등록 알림을 메일로 전송
- 알림 메일 발송 이력을 DBMS 저장
이렇게 보면
3 ~ 4가 실제 DB에 글을 저장하는 로직
7이 DB에 글을 저장하는 로직이다.
그리고 다른 부분은 이를 활용하는 것이거나 '6'번처럼 네트워크를 써서 문제가 생기는 경우가 있을 것이다.
- 처음부터 트랜잭션을 만들지 말고 필요할 때에 구성하여 사용하는것이 좋다.
잠금
- 스토리지 엔진 레벨 락
- MySQL 락
글로벌 락
- MySQL 락 중 가장 범위가 큰 락
- MySQL 서버 전체에 영향을 준다.
- 당연한거지만 락이 걸려있으면 다른 명령을 쓰지 못한다.
백업 락
- 위의 글로벌 락의 경우는 서버의 모든 변경 작업을 멈춘다.
- InnoDB에서 트랜잭션을 지원하기 때문에 데이터 상태를 위해 모든 변경 작업을 멈출 필요는 없다.
- 백업 툴들의 안정적인 실행을 위해 백업 락 도입됨.
- 특정 세션에서 백업락 획득하면 모든 세션에서 테이블의 스키마나 사용자의 인증 관련 정보를 변경할 수 없다.
- 일반적인 테이블의 데이터 변경은 허용된다.
테이블 락
- 개별 테이블 단위 잠금
- 명시적, 묵시적 락이 있는데(명시적 락은 명시적으로 해제 가능) 명시적 락은 특별한 경우가 아니면 잘 안쓰임
- 묵시적 락은 MyISAM이나 MEMORY테이블에 데이터를 변경하는 쿼리를 실행하면 발생
- 변경할때 잠금 -> 변경 즉시 해제
- 근데 InnoDB의 경우는 스토리지 차원에서 레코드 기반 잠금을 제공해서 테이블 락이 걸리지 않음
- DDL의 경우만 영향을 미침
- 묵시적 락은 MyISAM이나 MEMORY테이블에 데이터를 변경하는 쿼리를 실행하면 발생
네임드 락
GET_LOCK
함수를 통해 임의의 문자열에 대해 잠금 설정 가능- 대상이 테이블이나레코드 또는 데이터베이스 객체가 아니다.
- 사용자가 지정한 문자열에 대해 획득하고 반납하는 잠금
- 여러 클라이언트가 어떤 정보를 동기화하는 것처럼 상호 동기화 처리해야 할 때 요긴함
- 배치처럼 한번에 많은 레코드를 변경하는 쿼리는 데드락의 원인이 되고는 하는데 동일 데이터를 변경하거나 참조하는 프로그램끼리 분류해서 네임드 락을 걸면 해결이 쉽다
메타데이터 락
- DB객체(테이블이나 뷰 등)의 이름이나 구조를 변경하는 경우에 획득
- 얘는 자동 획득(명시적 획득 불가)
- Rename같은 경우
원본 이름
이랑바꿀 이름
둘다 잠금을 건다.
InnoDB 스토리지 엔진 잠금
얘는 레코드 기반 잠금 기능을 제공한다.
락 에스컬레이션은 없다.
InnoDB에서는 레코드 끼리의 간격을 잠그는 갭 락이라는 것도 있다.
레코드 락
- 레코드를 잠그는 것으로 다른 상용 DBMS의 레코드 락이랑 동일한 역할을 한다.
- 한가지 차이는 InnoDB에서는 인덱스의 레코드를 잠금
- 인덱스가 없어도, 자동 생성된 클러스터 인덱스를 이용해서 잠금을 실행한다.
- 보조 인덱스를 이용한 변경 -> 넥스트 키 락이나 갭 락 사용
- PK나 UK에 의한 변경 작업에서는 레코드 자체에만 락을 건다.
갭 락
- 레코드와 바로 인접한 레코드 사이의 간격만들 잠그는것
- 이거 역할은 저 레코드 사이에 새로운 레코드가 생성되는거를 제어하는것이다.
- 이거는 뒤에서 나오는 넥스트 키 락의 일부로 사용됨
넥스트 키 락
- 레코드락 + 갭락
- InnoDB의 갭락이나 넥스트 키 락은 바이너리 로그에 기록되는 쿼리가 레플리카 서버에서 실행될 때 소스 서버에서 만들어 낸 결과와 동일한 결과를 만들어내도록 보장하는 것이 주목적
자동 증가 락
- MySQL에서는 자동증가하는 숫자값을 추출하기 위해
AUTOO_INCREMENT
속성을 제공한다. - 이게 여러 레코드가 insert되면 서로 중복없이 저장된 순서대로 증가해야 하는데 이를 자동 증가 락이라 하는 테이블 수준 잠금을 사용함
- 참고로 자동 증가 값이 한번 증가하면 다시 줄어들지 않는 이유는 자동 증가 락의 잠금을 최소화하기 때문이다.
인덱스와 잠금
InnoDB의 잠금과 인덱스는 상당히 중요한 연관 관계가 있다.
- InnoDB의 잠금은 레코드가 아니라 인덱스를 잠그는 방식으로 처리된다.
- 변경해야 할 레코드를 찾기 위해 검색한 인덱스의 레코드를 모두 락을 걸아야 한다는 것
- 그래서 UPDATE작업을 하거나 할 때에 인덱스 설계가 매우매우매우매우 중요하다!!!
- 변경해야 할 레코드를 찾기 위해 검색한 인덱스의 레코드를 모두 락을 걸아야 한다는 것
레코드 수준의 잠금 확인 및 해제
- InnoDB의 레코드 수준 잠금은 테이블 수준 잠금보다는 조금 더 복잡한데, 레코드에 잠금이 걸리고 그게 오랫동안 잠겨져 있어도 잘 발견이 안된다.
- 요즘은(5.1버전 이상) 조회가 가능함 ㅇㅇ.
MySQL의 격리 수준
사실 이전에 한번 공부하기는 했는데 다시 봐보자
Dirty Read | Non-Repeatable Read | Phantom Read | |
---|---|---|---|
Read Uncommitted | 발생 | 발생 | 발생 |
Read Committed | 없음 | 발생 | 발생 |
Repeatable Read | 없음 | 없음 | 발생(InnoDB는 없음) |
Serializable | 없음 | 없음 | 없음 |
Read Uncommitted
- 각 트랜잭션 변경 내용이 commit이나 rollback여부와 관계 없이 보임
- Dirty Read 발생 가능
- 데이터가 롤백되었는데 그게 마치 있는 것처럼 보이는 현상
- 참고로 이 Read Uncommitted는 정합성에 많은 문제가 있어 아예 격리 수준으로 인정하지도 않는다고 한다.(그럴만함)
Read Committed
- 오라클 기본 격리수준 + 온라인 서비스에서 가장 많이 선택되는 격리 수준
- 이거는 Commit 완료된 데이터만 조회 가능하다.
- Non-Repeatable Read 발생 가능
- 한 트랜잭션 내에서 다른 트랜잭션의 커밋 시점에 의해 다른 결과를 가져오게 된다.
- 즉 Repeatable Read 정합성에 어긋난다.
- 금전처리처럼 하나의 트랜잭션에서 동일 데이터를 여러번 읽고 작업하는 경우 문제가 된다.
Repeatable Read
- MySQL의 InnoDB 스토리지 엔진에서 기본으로 사용
- 바이너리 로그를 가진 MySQL서버에서는 최소 Repeatable Read 격리 수준 이상을 사용해야 한다.
- 이거는 Non-Repeatable Read 부정합이 발생하지 않음
- 트랜잭션 Rollback에 대비해서 언두로그 백업하고 그 데이터를 이용해 동일 트랜잭션 내에서는 동일한 데이터를 보여준다.
- 트랜잭션이 각각의 트랜잭션 번호를 가져서(이게 순차적 증가임) 언두 영역 레코드에는 이 번호가 저장되어 있고 그거를 통해 데이터를 가져오면 되는것
- 이것도 근데 Phantom Read 현상 발생 가능
select for update
같은 경우는 select대상 레코드에 쓰기 잠금을 걸어야 하는데 언두 레코드에는 잠금을 걸 수 없다.- 그래서 이거는 언두영역의 변경 전 데이터가 아니라 현재 레코드의 값을 가져오는것
Serializable
- 가장 단순하고 엄격하다.
- 동시 처리 성능도 떨어짐
- 읽기 잠금에도 공유잠금을 걸어주는거
- 참고로 InnoDB에서 "Non-locking consistent read(잠금이 필요없는 일관된 읽기)"라는 말이 존재하는데, 얘는 본래 보통은 읽기에 잠금을 걸지 않는다는 것이다.
- 그래서 이거 락 걸어주면 성능이 떨어진다.
- 참고로 InnoDB에서 "Non-locking consistent read(잠금이 필요없는 일관된 읽기)"라는 말이 존재하는데, 얘는 본래 보통은 읽기에 잠금을 걸지 않는다는 것이다.
- 근데 InnoDB스토리지 엔진에서는 갭락과 넥스트 키 락 덕분에 Repeatable Read에서도 Phantom Read가 발생하지 않는다(select for update, select for share처럼 잠금을 동반한 select는 예외)
- 지금 트랜잭션에서 읽는 대상이 된 인덱스에 대해서는 데이터의 입력이 불가능하므로...
- 그러니까 그냥 InnoDB에서는 Repeatable Read를 쓰면 되는데 이걸 굳이 쓸 필요는 없다는 것
'이론 정리 > Database' 카테고리의 다른 글
Real MySQL 7장 정리 (1) | 2023.12.28 |
---|---|
Real MySQL 6장 정리 (3) | 2023.12.07 |
Real MySQL 4장 정리 (2) | 2023.12.02 |
레디스를 로컬이 아닌 외부에 두는 이유 (0) | 2023.10.20 |
MySQL 페이징 해보자(feat offset, infinite scroll) (0) | 2023.10.02 |