- 잠금 : 동시성 제어를 위한 기능
- 트랜잭션 : 데이터 정합성을 보장하기 위한 기능
- 트랜잭션의 격리 수준 : 하나 또는 여러 트랜잭션 간의 작업 내용을 공유하고 차단할 것인지 결정하는 레벨
- InnoDB 스토리지 엔진은 트랜잭션을 지원하는 스토리지 엔진 (MyISAM,MEMORY 스토리지 엔진은 미지원)
- 꼭 여러 쿼리가 조합되야만 의미가 있는것은 아님
- 논리적인 작업 Set이 100% 적용 되거나 아예 적용되지 않음을 보장해줌
- 트랜잭션은 최소한의 코드에만 적용하는 것이 좋다
- 트랜잭션은 빠르게 처리되어야 한다
- 커넥션은 개수가 제한적이기 때문에 소유 시간이 길어지면 성능에 영향을 준다.
- 메일 전송, FTP 파일 전송은 반드시 트랜잭션 밖에서 처리해야 한다.
- 메일서버,FTP 서버와 통신할 수 없는 경우 장애가 DBMS까지 번지게 된다.
- 잠금의 종류
- 스토리지 엔진 레벨의 잠금
- 스토리지 엔진 간 상호 영향 주지 않음
- MySQL 엔진 레벨의 잠금
- 모든 스토리지 엔진에 영향을 줌
- 명령어 : FLUSH TABLES WITH READ LOCK
- 범위 : MySQL 서버 전체
- 데이터베이스가 다르더라도 동일하게 영향을 미침
- 사용 목적 : MyISAM,MEMORY 테이블에 대해 mysqldump로 일관된 백업을 받아야 할 때 주로 사용
- 큰 영향을 미치기 때문에 가급적 사용하지 않는 것이 좋음
- 백업락 : MySQL 8.0 부터 Xtrabackup, Enterprise Backup 같은 백업 툴의 안정적인 실행을 위해 도입
- 일반적인 테이블의 데이터 변경은 허용 (DML)
- DDL 명령 실행 시 데이터 복제를 일시 중지
- 명령어 : LOCK TABLES table_name [READ | WRITE]
- 범위 : 테이블 단위
- 명시적으로 사용 가능
- 특별한 상황이 아니면 사용할 필요가 없다, 글로벌 락과 마찬가지로 온라인 작업에 상당한 영향을 미치기 때문
- 묵시적익 테이블 락
- MyISAM,MEMORY 테이블의 데이터를 변경하는 쿼리 실행시 발생
- InnoDB 테이블은 레코드 단위 락을 제공해 묵시적인 테이블락 사용 X
- InnoDB 엔진에서 DML 쿼리의 경우 대부분 무시, DDL 쿼리의 경우 테이블 락이 설정 됨
- 기능 : 사용자가 지정한 문자열에 대해 잠금을 설정
- 용도 : 복잡한 요건으로 레코드를 변경하는 트랜잭션에 유용
- 락 설정 명령어 : SELECT GET_LOCK('lock_name',timeout) -> 이미 잠금 중인 경우 timeout만큼 대기하고 잠금 해제
- 락 확인 명령어 : SELECT IS_FREE_LOCK('lock_name') -> 락이 설정되어 있는지 확인
- 락 해제 명령어 : SELECT RELEASE_LOCK('lock_name') / SELECT REALEASE_ALL_LOCKS()
- 기능 : 데이터베이스 객체(테이블, 뷰 등 )의 이름이나 구조를 변경하는 경우에 획득하는 잠금
- 명시적으로 사용 불가능
- DDL 쿼리 실행시 자동으로 획득
- DDL 쿼리 실행 중에는 DML 쿼리가 대기
- MySQL은 한 쓰레드에서 실행되기 때문에 느림
- DDL 실행하기 보다 테이블을 여러 쿼리를 통해 여러 쓰레드에서 복제한 후 RENAME하는 것이 빠를 수 있음
- InnoDB 스토리지 엔진은 트랜잭션을 지원하는 스토리지 엔진
- InnoDB 스토리지 엔진은 레코드 단위 락을 제공
- 트랜잭션 잠금, 잠금 대기 중인 트랜잭션 모니터링 기능 제공
- infroamtion_schema.INNODB_TRX, INNODB_LOCKS, INNODB_LOCK_WAITS 테이블을 통해 확인 가능
- 레코드 단위로 잠금을 설정
- 실제 레코드를 잠그는 것이 아니라 레코드를 조회하기 위한 인덱스를 잠근다.
- 보조 인덱스를 활용한 잠금은 대부분 갭락, 넥스트 키 락을 사용한다.
- PK, 유니크 인덱스를 활용한 잠금은 레코드 락을 사용한다.
- 레코드가 아니라 레코드와 레코드의 인접한 간격을 잠그는 락
- 갭락 자체보다는 넥스트 키 락의 일부로 사용됨
- 레코드락 + 갭락
- 넥스트 키 락, 갭 락 으로 인한 데드락이 자주 발생하게 됨
- AUTO_INCREMENT 컬럼에 대한 락
- INSERT,REPLACE 쿼리 같이 새로운 레코드 생성 쿼리에서만 사용
- 명시적 사용 X
- 아주 짧은 AUTO_INCREMENT 값을 조회하는 동안에만 락이 설정되어 있음
- mode에 따라 락을 사용하거나 락보다 가벼운 래치(뮤텍스)를 사용한다.
- 한 번 증가하면 줄지 않는다. (잠금을 최소화하기 위함)
- InnoDB 스토리지 엔진은 레코드 단위 락을 제공
- 보조 인덱스를 사용해 조회하는 경우 레코드가 아닌 인덱스에 잠금을 걺
- ex)
- 상황1 : SELECT COUNT(*) FORM employees WHERE FIRST_NAME = 'SANGHOO' => 253
- 상황2 : SELECT COUNT(*) FORM employees WHERE FIRST_NAME = 'SANGHOO' AND LAST_NAME = 'PARK' => 1
- 상황3 : UPDATE employees SET SALARY = 100000 WHERE FIRST_NAME = 'SANGHOO' AND LAST_NAME = 'PARK'
- 상황3에서 업데이트를 위한 잠금을 얻는다.
- 이 때 한 개의 레코드에만 락이 걸리는 것이 아니라 FIRST_NAME을 조회하기 위한 인덱스에 락이 걸리게 된다.
- FIRST_NAME = 'SANGHOO'인 253개의 레코드에 전체 락이 걸림
- 레코드 수준의 잠금은 범위가 작기 때문에 잠금을 해제 하지 않은 경우 잘 발견되지 않는다.
- SHOW PROCESSLIST 명령어를 통해 pending된 trx 확인 가능
- KILL 명령어를 통해 강제로 해제 가능
- performance_schema.data_locks, data_lock_waits 테이블을 통해 확인 가능
- ex)
- 17 trx가 커밋되지 않은 상황
- 18 trx,19 trx가 17 trx를 기다리고 있음
- performance_schema.data_lock_waits 확인 시 18은 17을 19는 17,18을 대기하고 있음
- 격리 수준 : 여러 트랜잭션이 동시 처리될 때 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있게 허용할지 말지 결정하는 것
- READ UNCOMMITTED -> READ COMMITTED -> REPEATABLE READ -> SERIALIZABLE
- 격리 수준이 높아질수록 동시처리 성능 은 떨어지지만 데이터 정합성은 높아진다.
- SELIALIZABLE은 가장 높은 격리 수준이지만 가장 느리다. (나머지는 크게 성능 개선,차이가 발생하지 않음)
- 어떤 트랜잭션에서 다른 트랜잭션의 변경되지 않은 데이터를 읽는 현상
- REPEATABLE READ : 하나의 트랜잭션 내에서 동일한 SELECT 쿼리를 실행했을 때 항상 같은 결과를 가져와야한다
- REAPEATABLE READ 정합성을 보장하지 않는 현상
- 하나의 트랜잭션 동안 동일한 쿼리를 여러 번 실행했을 때 결과로 나타나는 행의 집합이 변경되는 현상
- 발생원인 : 다른 트랜잭션이 쿼리가 실행되는 도중에 해당 쿼리의 결과에 영향을 줄 수 있는 새로운 행을 삽입하거나 삭제할 때 발생
- 언두 로그에는 잠금을 걸 수 없기 때문에 발생
- 다른 트랜잭션이 커밋하지 않은 데이터를 읽을 수 있음
- Dirty Read 발생 가능
- 언두 로그가 아닌 InnoDB 버퍼풀의 데이터를 바라보고 있다.
- 최소한 READ COMMITTED 이상의 격리 수준을 사용할 것을 권장
- 다른 트랜잭션이 커밋한 데이터만 읽을 수 있음
- Dirty Read 발생하지 않음
- 커밋되지 않은 TRX의 경우 언두 영역에 백업된 변경되기 전 데이터를 읽어옴
- Non-Repeatable Read 부정합이 발생
- InnoDB 스토리지 엔진의 기본 격리 수준
- 같은 트랜잭션 내에서는 항상 같은 조회 결과를 보장
- READ COMMITTED, REPEATABLE READ 모두 언두 로그를 이용해 MVCC를 구현
- 둘의 차이 : 언두 영역에 백업된 레코드 중 몇 번째 이전 버전까지 찾아 들어가느냐
- 언두 로그에는 변경을 발생시킨 트랜잭션 번호가 포함됨
- REPEATABLE READ : 자신의 트랜잭션 번호보다 작은 트랜잭션 번호에서 변경된 것만 언두로그에서 읽음
- Phantom Read 부정합 발생 가능
- MySQL InnoDB의 경우 갭락, 넥스트 키 락 덕분에 Phantom Read가 발생하지 않음
- 가장 높은 격리 수준
- 읽기 작업도 공유 잠금 권한을 획득해야함
- MySQL InnoDB에서는 REPEATABLE READ에서도 Phantom Read가 발생하지 않아 굳이 사용할 이유가 없음
-
InnoDB도 Phantom Read가 발생할 수 있다.
-
트랜잭션 ACID
-
Atomicity : 원자성
- 트랜잭션 내 작업은 하나처럼 동작한다.
-
Consistency : 일관성
- 트랜잭션이 완료되면 일관적인 DB 상태를 유지한다.
- 설정한
-
Isolation : 격리성 ??
- 고립성은 동시 트랜잭션을 처리하는데 사용
-
Durability : 지속성
- 트랜잭션이 성공적으로 완료되면 영구적으로 DB에 반영된다.
-
InnoDB는 Phatom Read가 발생하지 않는다 ??
- 발생한다 !
- SELECT / SELECT ... FOR UPDATE 하는 경우 ! (언두 로그를 읽고 InnoDB버퍼 풀을 읽기 때문에 !)