학습 내용
- MySQL의 동시성에 영향을 미치는 잠금(Lock) 과 트랜잭션, 격리수준
트랜잭션
- 작업의 완전성 보장
- 데이터의 정합성 보장
- 논리적인 작업 셋을 모두 완벽하게 처리하고나, 실패하면 모두 원 상태로 복구해서 부분 성공이나 부분 실패를 방지하는 기능
잠금
- 동시성 제어하기 위한 기능
- 하나의 레코드에 동시에 여러 트랜잭션이 접근할 때 제어 기능을 제공한다.
MySQL 엔진의 잠금
- 스토리지 엔진 레벨
- MySQL 엔진 레벨
- MySQL 엔진 레벨의 잠금은 스토리지 엔진에 영향을 미친다.
- 메타데이터 락
- 네임드 락
글로벌 락
- 한 세션에서 글로벌 락을 걸면 SELECT 를 제외한 모든 작업에 대해 대기 상태로 만든다.
- MySQL 8부터 가벼운 글로벌 락 도입
- 백업 락
- 잠금 대상
- 데이터베이스 및 테이블 등 모든 객체 생성 및 변경, 삭제
- REPAIR TABLE, OPTIMIZE TABLE
- 사용자 관리 및 비밀번호 변경
테이블 락
- 테이블 단위에 락을 거는 것
- 묵시적 테이블 락 : 쿼리 실행 후 종료까지 자동으로 획득했다가 쿼리가 종료되면 해제하는 것이다.
- InnoDB 엔진에서는 레코드 기반 잠금을 지원하므로 묵시적인 테이블 락이 설정되지 않는다.
- 테이블락은 DML 에서는 무시되고 DDL 에서만 영향을 미친다.
네임드 락 ?
- 데이터베이스 객체가 아니라 단순히 사용자가 지정한 문자열에 대해 획득하고 반납하는 잠금
메타데이터 락
- 데이터베이스 객체의 이름이나 구조를 변경하는 경우에 획득하는 잠금
- 명시적으로 획득하는게 아니라 이름이나 구조 변경 작업 시 자동 획득한다.
InnoDB 스토리지 엔진 잠금
- 스토리지 엔진 내부에서 레코드 기반의 잠금 방식을 탑재하고 있다.
InnoDB 스토리지 엔진 잠금
- 잠금 정보가 상당히 작은 공간으로 관리되기 때문에 락 에스컬레이션은 없다.
레코드 락
- 레코드 자체만을 잠그는 것
- 잠금 대상은 레코드 자체가 아니라 인덱스의 레코드를 잠근다.
- 보조 인덱스를 이용한 변경 작업은 넥스트 키 락 또는 갭 락을 사용
- PK 또는 유니크 인덱스에 의한 변경 작업에서는 레코드 자체에 대해서만 락을 건다.
갭 락
- 레코드 자체가 아니라 레코드와 바로 인접한 레코드 사이의 간격만을 잠그는 것
- 역할 : 레코드와 레코드 사이의 간격에 새로운 레코드가 생성되는 것을 제어
- 넥스트 키 락의 일부로 자주 사용된다.
넥스트 키 락
갭 락과 넥스트 키 락의 목적
- 바이너리 로그에 기록되는 쿼리가
*바이너리 로그
*
- 데이터 변경사항들에 대한 정보를 포함하는 로그 파일의 세트
- 목적
- MySQL 서버 장애 시 복구를 위해 사용
- MySQL Replication 구성을 위해 사용
- 레플리카 서버에서 실행될 때
*레플리카 서버*
: 복제된 데이터를 가지는 서버
- 소스 서버에서 만들어낸 결과와
*소스 서버
: 원본 데이터를 가진 서버를 소스(Source) 서버*
- 동일한 결과를 만들어내도록 보장하는 것이 주 목적
- STATEMENT 포맷의 바이너리 로그를 사용하는 MySQL 서버에서는 Repeatable read 격리 수준을 사용해야 한다.
- 8 버전에서는 ROW 포맷 바이너리 로그가 기본 설정
STATEMENT 포맷 바이너리 로그
- Insert, Update, Delete 에 대한 SQL 문들이 포함된다. Statement base 로 복제를 수행 시 Statement-Based Replication (SBR) 이라고 한다.
ROW 포맷 바이너리 로그
- 이 방식은 각 행에 대한 변화를 기록한다. Row-based logging 을 이용해서 Primary → Secondary 로 복제를 수행할 수 있고, 이를 Row-Based Replication(RBR)이라 한다
자동 증가 락
- AUTO_INCREMENT 칼럼이 사용된 테이블에 동시에 여러 레코드가 INSERT 되는 경우,
저장되는 각 레코드는 중복되지 않고 저장된 순서대로 증가하는 일련번호 값을 가져야 한다.
- InnoDB 스토리지 엔진에서는 이를 위해 내부적으로 AUTO_INCREMENT 락이라 하는 테이블 수준의 잠금을 사용한다.
- 트랜잭션과 관계 없이 auto_increment 값을 가져오는 순간만 락이 걸렸다 즉시 해제된다.
- 자동 증가 값이 한 번 증가하면 절대 줄어들지 않는 이유는 auto_increment 잠금을 최소화하기 위해서다.
인덱스와 잠금
- InnoDB의 잠금과 인덱스는 상당히 중요한 연관 관계가 있다.
- InnoDB 에서 인덱스 설계가 중요한 이유는 레코드 자체를 잠그는 것이 아니라 인덱스를 잠그기 때문이다.
레코드 수준의 잠금 확인 및 해제
- 레코드 수준의 잠금은 오랜 시간 동안 잠겨진 상태로 남아 있어도 발견이 안 될 수도 있다.
- MySQL 5.1 부터 잠금과 잠금 대기에 대한 조회를 제공하고 있다.
MySQL 의 격리 수준
- 격리 수준이란 한 트랜잭션의 변경 내용을 다른 트랜잭션에 어떻게 보여줄 지 설정하는 것이다.
- 종류
- READ UNCOMMITED ( DIRTY READ )
- READ COMMITED
- REPEATABLE READ
- SERIALIZABLE
- 주로 read commited, repeatable read 를 사용한다.
READ UNCOMMITED ( DIRTY READ )
- 정의 : 한 트랜잭션의 변경 내용을 커밋 하기 전에도 다른 트랜잭션에서도 보이는 것
READ COMMITED
- 정의 : 한 트랜잭션의 변경 내용을 커밋 하기 전에는 다른 트랜잭션에서 볼 수 없는 것
- 오라클 DBMS 기본 격리 수준 / 많이 채택하는 격리 수준
- 다른 트랜잭션은 언두 영역에 있는 변경 전 데이터를 조회하게 된다.
- Non-Repeatable-Read 발생
REPEATABLE READ
-
정의 : 한 트랜잭션 내에서 같은 SELECT 여러번 해도 결과가 동일해야 한다.
-
MySQL 의 InnoDB 스토리지 엔진의 기본 격리 수준
-
REPEATABLE READ 과 READ COMMITED 의 차이
언두 영역에 백업된 레코드의 여러 버전 가운데 몇 번째 이전 버전까지 찾아 들어가야 하느냐에 있음.
- 모든 InnoDB 트랜잭션은 고유한 트랜잭션 번호를 가진다.
- 언두 영역에 백업된 모든 레코드에는 변경을 발생시킨 트랜잭션 번호가 포함돼 있다.
-
Phantom read 발생 : 다른 트랜잭션에서 수행한 변경 작업에 의해 레코드가 보였다 안 보였다 하는 현상
[출처 : Real MySQL 1권]
- SELECT … FOR UPDATE 는 언두 로그에 잠금을 걸 수 없으므로 현재 레코드의 값을 가져오기 때문.
-
InnoDB 스토리지 엔진에서는 갭 락과 넥스트 키 락 덕분에 Phantom read 발생하지 않는다.
SERIALIZABLE
- 정의 : 한 트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서는 절대 접근할 수 없는 것
- 이 수준에서는 읽기 작업도 공유 잠금을 획득해야 한다.
컴퓨터 정보 단위
- 비트 : 0과 1을 나타내는 가장 작은 정보 단뒤
- 바이트 : 여덟 개의 비트 / 표현 범위 2^8 (256)개
- 킬로바이트 : 1바이트 1,000개
- 메가바이트 : 1킬로바이트 1,000개
- 기가바이트 : 1메가바이트 1,000개
- 테라바이트 : 1기가바이트 1,000개
word
CPU 가 한 번에 처리할 수 있는 단위
ex) cpu 가 한 번에 16비트를 처리할 수 있다면 1워드는 16비트가 된다.
이진법
0과 1로만 숫자를 표현하는 방법
- 이진수의 음수 표현
- 컴퓨터는 0과 1만 이해할 수 있으므로 마이너스 부호를 사용하지 않고 0과 1로 표현해야 한다.
- 2의 보수 : 가장 널리 사용되는 방법
- 컴퓨터는 음수,양수를 구분하기 위해 플래그를 사용한다.
십육진법
- 이진법으로 표현을하면 숫자의 길이가 너무 길어진다. 그래서 십육진법도 자주 사용한다.
- 십육진법은 15를 넘어가는 시점에 자리 올림하여 수를 표현하는 방법이다.
고급언어
저급언어
-
컴퓨터가 직접 이해하고 실행할 수 있는 언어
-
종류 : 기계어, 어셈블리어
기계어
0과 1의 명령어 비트로 이루어진 언어.
어셈블리어
기계어는 사람이 읽으면 의미를 이해하기 어렵니다. 그래서 등장한 저급 언어가 어셈블리어다.
0101 0101 ----> push rbp
컴파일 언어
컴파일러에 의해 소스 코드 전체가 저급 언어로 변환되어 실행되는 고급 언어
- 목적코드 : 컴파일러를 통해 저급 언어로 변환된 코드
인터프리터 언어
인터프리터에 의해 소스 코드가 한 줄씩 실행되는 고급 언어