트랜잭션이란?
작업의 완전성을 보장해 주는 것
논리적인 작업 셋을 모두 완벽하게 처리하거나,
처리하지 못할 경우에는 원 상태로 복구해서
작업의 일부만 적용되는 현상(paritial upadte)이 발생하지 않게 만들어주는 기능이다.
잠금과 트랜잭션의 차이
잠금 : 동시성을 제어하기 위한 기능
트랜잭션 : 데이터의 정합성을 보장하기 위한 기능
* 격리수준 : 하나의 트랜잭션 내에서 또는 여러 트랜잭션 간의 작업 내용을
어떻게 공유하고 차단할 것인지를 결정하는 레벨을 의미한다.
MySQL에서의 트랜잭션
- 트랜잭션은 꼭 여러개의 변경 작업을 수행하는 쿼리가 조합됐을 때만 의미 있는 개념이 아니라,
하나의 논리적인 작업 셋에 하나의 쿼리가 있든 두개의 쿼리가 있든 관계없이 논리적인 작업 셋 자체가 100% 적용되거나(COMMIT을 실행하거나) 아무것도 적용되지 않아야(ROLLBACK 또는 트랜잭션을 ROLLBACK시키는 오류가 발생했을때) 함을 보장해 주는 것이다.
- INSERT 쿼리가 프라이머리 키 중복 오류로 쿼리가 실패했을 경우
MyISAM : 해당 키 중복 오류인 값만 빼고 나머지는 INSERT
InnoDB : 일부라도 오류가 발생한 경우 전체를 원상태로 만든다는 트랜잭션의 원칙대로 INSERT 문장 실행 전으로 복구
=> MyISAM에서 발생하는 문제를 부분 업데이트라고 부르며, 테이블 데이터의 정합성을 맞추는데 상당히 어려운 문제를 만들어 낸다.
트랜잭션 사용 시 주의사항
- 프로그램의 코드가 데이터베이스 커넥션을 가지고 있는 범위와 트랜잭션이 활성화 돼 있는 프로그램의 범위를 최소화해야 한다는 것이다.
MySQL 엔진의 잠금
- MySQL에서 사용되는 잠금은 스토리지 엔진 레벨과 MySQL 엔진 레벨로 나눌 수 있다
- MySQL 엔진은 모든 스토리지 엔진에 영향을 미치지만, 스토리지 엔진 레벨의 잠금은 스토리지 엔진 간 상호 영향을 미치지는 않는다.
1) 글로벌 락
- FLUSH TABLES WITH REAB LOCK과 같은 명령으로 획득 가능한 락이다
- MySQL에서 제공하는 잠금 가운데 가장 범위가 크다. MySQL 서버 전체에 영향을 미치는 락이다.
- MyISAM이나 MEMORY 테이블에 대해 mysqldump로 일관된 백업을 받을 때 글로벌 락을 사용한다
- InnoDB 스토리지 엔진을 쓰면 트랜잭션을 지원하기 때문에 일관된 데이터 상태를 위해 모든 데이터 변경 작업을 멈출 필요는 없다
2) 테이블 락 ****
- 개별 테이블 단위로 설정되는 잠금이다.
- 명시적 또는 묵시적으로 특정 테이블의 락을 획득할 수 있다
- 명시적으로는 LOCK TABLES table_name [ READ | WRITE ] 명령으로 특정 테이블의 락을 획득할 수 있다
=> 명령어로 락 해제가 가능하다
- 묵시적으로는 MyISAM이나 MEMORY 테이블에 데이터를 변경하는 쿼리를 실행하면 발생한다.
=> MySQL 서버가 데이터가 변경되는 테이블에 잠금을 설정하고 데이터를 변경한 후 , 즉시 잠금을 해제하는 형태로 사용된다. 데이터 변경 후에 쿼리 완료된 후 자동해제된다.
- InnoDB 테이블의 경우 스토리지 엔진 차원에서 레코드 기반의 잠금을 제공하기 때문에 단순 데이터 변경 퉈리로 인해 묵시적인 테이블 락이 설정되지는 않는다. (설정되긴 하는데 DML 쿼리에서는 무시하고 DDL 쿼리의 경우에만 영향을 미친다)
3) 네임드 락
- GET_LOCK() 함수를 이용해 임의의 문자열에 대해 잠금을 설정할 수 있다
- 여러 클라이언트가 상호 동기화를 처맇야 할때 네임드 락을 이용하면 쉽게 해결할 수 있다
- 동일데이터를 변경하거나 참조하는 프로그램끼리 분류해서 네임드 락을 걸고 쿼리를 실행하면 아주 간단히 해결할 수 있다.
4) 메타데이터 락
- 데이터베이스 객체(대표적으로 테이블이나 뷰 등)의 이름이나 구조를 변경하는 경우에 획득하는 잠금이다.
- 메타데이터 락은 명시적으로 획득하거나 해제할 수 있는 것이 아니고 "RENAME TABLE tab_a to tab_b" 같이 테이블의 이름을 변경하는 경우 자동으로 획득하는 잠금이다
- 메타데이터 잠금과 트랜잭션을 동시에 사용해야할 때도 있다
InnoDB 스토리지 엔진 잠금
- InnoDB 스토리지 엔진은 MySQL에서 제공하는 잠금과는 별개로 스토리지 엔진 내부에서 레코드 기반의 잠금 방식을 탑재하고 있다.
- InnoDB 스토리지 엔진은 레코드 기반의 잠금 기능을 제공하며, 잠금 정보가 상당히 작은 공간으로 관리되기 때문에 레코드 락이 페이지 락으로, 또는 테이블 락으로 레벨업되는 경우는 없다.
1) 레코드 락
- 레코드 자체만을 잠그는 것을 레코드 락이라고 하며, 다른 상용 DBMS락과 동일한 역할을 한다.
- 한가지 중요한 차이는 InnoDB 스토리지 엔진은 레코드 자체가 아니라 인덱스의 레코드를 잠근다는 점이다
(인덱스가 하나도 없는 테이블이더라도 내부적으로 자동 생성된 클러스터 인덱스를 이용해 잠금을 설정한다)
2) 갭 락
- 레코드 자체가 아니라 레코드와 바로 인접한 레코드 사이의 간격만을 잠그는 것을 의미한다
- 갭락의 역할은 레코드와 레코드 사이의 간격에 새로운 레코드가 생성되는 것을 제어하는 것이다.
3) 넥스트 키 락
- 레코드 락과 갭 락을 합쳐 놓은 형태의 잠금을 넥스트 키 락이라고 한다.
4) 자동 증가 락
- AUTO_INCREMENT 칼럼이 사용된 테이블에 동시에 여러 레코드가 INSERT되는 경우, 저장되는 각 레코드는 중복되지 않고 저장된 순서대로 증가하는 일련버호 값을 가져야 한다.InnoDB 스토리지 엔진에서는 이를 위해 내부적으로 AUTO_INCREMENT 락이라고 하는 테이블 수준의 잠금을 사용한다.
- INSERT와 REPLACE 쿼리 문장과 같이 새로운 레코드를 저장하는 쿼리에서만 필요하며, UPDATE나 DELETE 등의 쿼리에서는 걸리지 않는다.
- AUTO_INCREMENT 락을 명시적으로 획득하고 해제하는 방법은 없다.
인덱스와 잠금
- InnoDB의 잠금은 레코드를 잠그는 것이 아니라 인덱스를 잠그는 방식으로 처리된다.
- 즉, 변경해야 할 레코드를 찾기 위해 검색한 인덱스의 레코드를 모두 락을 걸어야 한다.
- 두가지 where 절을 걸고 update를 친다면 그 중 인덱스걸린 칼럼을 찾아서 레코드 락을 건다
레코드 수준의 잠금 확인 및 해제
- 음,, 확인하는 방법이 책에 설명되어있긴 함
MySQL의 격리 수준
- 트랜잭션의 격리 수준이란 여러 트랜잭션이 동시에 처리될 떄 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있게 허용할지 말지를 결정하는 것이다
- 격리 수준은 크게 "READ UNCOMMITTED" "READ COMMITED" "REPEATABLE READ" "SERIALIZABLE"
1) READ UNCOMMITTED
- 어떤 트랜잭션에서 처리한 작업이 완료되지 않았는데도 다른 트랜잭션에서 볼 수 있는 현상을 더티 리드라 하고, 더티 리드가 허용되는 격리 수준이 READ UNCOMMITED.
- 더티 리드를 유발하는 이 격리 수준은 정합성에 문제가 많은 격리 수준이다
- MySQL을 사용한다면 얘는 쓰지마라
2) READ COMMITED
- 오라클 DBMS의 기본으로 사용되는 격리 수준이다.
- 이 레벨에선 더티 리드 같은 현상은 발생하지 않는다.
- 어떤 트랜잭션에서 데이터를 변경했더라도 COMMIT이 완료된 데이터만 다른 트랜잭션에서 조회할 수 있기 때문이다.
- 하지만 이 격리 수준에서는 하나의 트랜잭션에서 똑같은 SELECT 쿼리를 실행했을 때는 항상 같은 결과를 가져와야 한다는 "REPEATABLE READ" 정합성에 어긋난다
3) REPEATABLE READ
- InnoDB 스토리지 엔진에서 기본으로 사용되는 격리수준이다
- 트랜잭션이 ROLLBACK될 가능성에 대비해 변경되기 전, 레코드를 언두 공간에 백업해두고 실제 레코드 값을 변경한다.이러한 변경 방식을 MVCC라고 하며, 이 MVCC를 위해 언두 영역에 백업된 이전 데이터를 이용해 동일 트랜잭션 내에서는 동일한 결과를 보여줄 수 있게 보장한다.
- REPEATABLE READ 또한 MVCC를 이용해 COMMIT 되기 전의 데이터를 보여준다
- READ COMMITED와 REPEATABLE READ의 차이는 언두 영역에 백업된 레코드의 여러버전 가운데 몇번쨰 이전 버전까지 찾아 들어가느냐의 차이에 있다.
- READ 수준에서는 MVCC를 보장하기위해 실행 중인 트랜잭션 가운데 가장 오래된 트랜잭션 번호보다 트랜잭션 번호가 앞선 언두 영역의 데이터는 삭제할 수 없다
- 정확하게는 특정 트랜잭션 번호의 구간 내에서 백업된 언두 데이터가 보존돼야한다
4) SERIALIZABLE
- 가장 단순한 격리 수준이면서 동시에 가장 엄격한 격리 수준이다
- SELCT는 아무런 레코드 잠금도 설정하지 않고 실행되는데, 하지만 읽기 작업도 공유 잠금(읽기 잠금)을 획득해야만 하며, 동시에 다른 트랜잭션은 그러한 레코드를 변경하지 못하게된다.
- 즉, 한 트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서는 절대 접근할 수 없는 것이다.
- 근데.. InnoDB 스토리지 엔진에서는 갭 락과 넥스트 키 락 덕분에 REPEATABLE READ 수준에서도 PHANTOM READ가 발생하지 않기 때문에 굳이 쓸 필요는 없어보인다
'DB > MySQL' 카테고리의 다른 글
[MYSQL] Real MySQL 9장 - 옵티마이저 (0) | 2024.03.13 |
---|---|
[MYSQL] Real MySQL 8장 - 인덱스 (0) | 2024.03.10 |
[MYSQL] Real MySQL 4장 - 아키텍처 (0) | 2024.02.19 |