Skip to content

Commit

Permalink
5장
Browse files Browse the repository at this point in the history
  • Loading branch information
minsoozz authored Oct 17, 2024
1 parent 4fc5c61 commit 4b140ac
Showing 1 changed file with 127 additions and 0 deletions.
127 changes: 127 additions & 0 deletions 05장/트랜잭션과_잠금_김민수.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
## 트랜잭션

트랜잭션은 논리적인 작업 셋 자체가 100% 적용되거나 아무것도 적용되지 않아야 함을 보장해주는 것이다.

AUTO-COMMIT 모드에서 테스트를 하면 MyISAM은 데이터가 저장된 이후 오류가 발생하지만, InnoDB의 경우 쿼리 중 일부라도 오류가 발생하면 INSERT 문장을 실행하기 전으로 복구한다.

### 최적의 트랜잭션 설계

- 데이터베이스 커넥션을 가지고 있는 범위와 트랜잭션이 활성화되어 있는 범위를 최소화해야 한다
- 네트워크 작업이 있는 경우, 반드시 트랜잭션에서 배제해야 한다
- DBMS 서버 높은 부하 상태에 빠지거나 위험산 상태에 빠지는 경우가 빈번하다

## MySQL 엔진의 잠금

MySQL에서 사용되는 잠금은 크게 스토리지 엔진 레벨과 MySQL 엔진 레벨로 나눌 수 있다. MySQL 엔진 레벨의 잠금은 모든 스토리지 엔진에 영향을 미치지만, 스토리지 엔진 레벨의 잠금은 스토리지 엔진 간에 상호 영향을 미치지 않는다.

### 글로벌 락

- FLUSH TABLES WITH READ LOCK 명령으로 획득 가능
- MySQL에서 제공하는 잠금 중 범위가 가장 큼
- 한 세션에서 글로벌 락 획득 → 글로벌 락이 해제될 때까지 대기
- 영향을 미치는 범위는 서버 전체
- 대상 테이블이나 데이터베이스가 다르더라도 동일하게 영향을 미침

### 테이블 락

- 개별 테이블 단위로 설정되는 잠금
- LOCK TABLES table_name READ | WRITE 명령으로 특정 테이블의 락 획득 가능
- UNLOCK TABLES 명령으로 반납 가능
- 묵시적 테이블 락이 발생하는 경우
- MyISAM, MEMORY 테이블에 데이터 변경 쿼리를 실행할 때
- InnoDB 테이블에 스키마를 변경하는 DDL 쿼리를 실행할 때

### 네임드 락

- GET_LOCK() 함수를 이용해 임의의 문자열에 대한 잠금을 설정
- 자주 사용되지 않음
- 유용하게 사용 가능한 경우
- 여러 클라이언트가 상호 동기화를 처리해야 할 때
- 많은 레코드에 대해 복잡한 요건으로 레코드를 변경해야 할 때

### 메타데이터 락

- 데이터베이스 객체의 이름이나 구조를 변경하는 경우 자동으로 획득하는 잠금
- RENAME TABLE 명령의 경우, 원본 이름과 변경될 이름 모두 한꺼번에 잠금을 설정함
- 2개로 나눠 실행하면 아주 짧은 순간이지만 테이블이 존재하지 않는 순간이 생겨 오류가 발생함
- 테이블의 구조를 변경해야 하는 경우
- 새로운 구조의 테이블 생성 → 최근 데이터까지는 id 값을 범위 별로 나눠서 여러 개의 스레드로 빠르게 복사
- 나머지 데이터를 복사할 때 트랜잭션과 테이블 잠금, 그리고 RENAME TABLE 명령으로 응용 프로그램의 중단 없이 실행할 수 있음

## InnoDB 스토리지 엔진 잠금

스토리지 엔진은 MySQL에서 제공하는 잠금과 별개로, 스토리지 엔진 내부에서 레코드 기반의 잠금 방식을 탑재하고 있다. 때문에 MyISAM보다 훨씬 뛰어난 동시성 처리를 제공한다.

### InnoDB 스토리지 엔진의 잠금

- InnoDB 스토리지 엔진은 레코드 기반의 잠금 기능을 제공
- 레코드와 레코드 사이의 간격을 잠그는 갭 락 존재

### 레코드 락

- 레코드 자체만을 잠그는 것
- InnoDB 스토리지 엔진은 레코드 자체가 아닌 인덱스의 레코드를 잠금
- 인덱스가 하나도 없는 테이블 → 내부적으로 자동 생성된 클러스터드 인덱스를 이용해 잠금을 설정
- 프라이머리 키 또는 유니크 인덱스에 의한 변경 작업 → 레코드 자체에만 락을 검
- 보조 인잭스를 이용한 변경 작업 → 넥스트 키 락 또는 갭 락을 사용

### 갭 락

- 레코드 사이의 간격만을 잠그는 것
- 레코드와 레코드 사이의 간격에 새로운 레코드가 생성되는 것을 제어
- 넥스트 키 락의 일부로 자주 사용됨

### 넥스트 키 락

- 레코드 락과 갭 락을 합쳐 놓은 형태의 잠금

### 자동 증가 락

- AUTO_INCREMENT 컬럼이 사용된 테이블에서 사용하는 테이블 수준의 잠금
- 동시에 여러 레코드가 INSERT 되는 경우, 저장되는 레코드가 중복되지 않고 순서대로 증가하는 일련번호 값을 가지기 위해 사용함
- 자동 증가 값이 한 번 증가하면 절대 줄어들지 않음

### 인덱스와 잠금

- InnoDB의 잠금은 인덱스를 잠그는 방식으로 처리됨
- 변경해야 할 레코드를 찾기 위해 검색한 인덱스의 레코드에 모두 락을 걸어야 함
- 테이블에 인덱스가 없다면 테이블을 풀 스캔하면서 UPDATE 작업을 함
- 이때, 테이블에 있는 모든 레코드를 잠그게 됨

MySQL의 InnoDB에서 인덱스 설계가 중요한 이유

### 레코드 수준의 잠금 확인 및 해제

- 테이블 잠금은 잠금의 대상이 테이블 자체 → 문제의 원인이 쉽게 발견되고 해결될 수 있음
- 레코드 수준의 잠금은 테이블의 레코드 각각에 잠금 → 해당 레코드가 자주 사용되지 않는다면 오랜 시간 잠겨진 상태로 남아있어도 잘 발견되지 않음
- 강제로 잠금을 해제하려면 KILL 명령어를 통해 MySQL 서버의 프로세스를 강제 종료하면 됨

## MySQL의 격리 수준

트랜잭션의 격리 수준은 여러 트랜잭션이 동시에 처리될 때, 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있게 허용할지 말지를 결정하는 것이다. 격리 수준이 높을수록 데이터의 격리 정도가 높아지고 동시 처리 성능도 떨어진다.

### READ UNCOMMITTED

- 각 트랜잭션에서의 변경 내용이 COMMIT이나 ROLLBACK 여부에 상관없이 다른 트랜잭션에서 보인다
- Drity read
- 트랜잭션에서 처리한 적업이 완료되지 않았는데도 다른 트랜잭션에서 볼 수 있는 현상

### READ COMMITTED

- 오라클 DBMS의 기본 격리수준
- Drity read 현상은 발생하지 않음
- COMMIT이 완료된 데이터만 다른 트랜잭션에서 조회할 수 있기 때문
- 하나의 트랜잭션 내에서 똑같은 SELECT 쿼리를 실행했을 때 항상 같은 결과를 가져오지 못하는 문제가 있음

### REPEATABLE READ

- MySQL InnoDB 스토리지 엔진 기본 격리수준
- MVCC를 위해 언두 영역에 백업된 데이터를 이용해 트랜잭션 내에서는 동일한 결과를 보여줄 수 있도록 보장함
- Phantom read
- SELECT FOR UPDATE 쿼리를 여러 번 수행하면 결과라 다름
- 다른 트랜잭션에서 수행한 변경 작업에 의해 레코드가 보였다 안보였다 하는 현상

### SERIALIZABLE

- 가장 단순하면서 가장 엄격한 격리수준
- 동시 처리 성능도 다른 트랜잭션 격리 수준보다 떨어짐

0 comments on commit 4b140ac

Please sign in to comment.