March 09, 2022
잠금은 동시성을 제어하기 위한 기능이고, 트랜잭션은 데이터 정합성을 보장하기 위한 기능이다.
잠금이 없다면 하나의 데이터를 여러 커넥션에서 동시에 변경할 수 있게 되어, 결과적으로 해당 데이터의 값을 예측할 수가 없게 된다.
즉 잠금은 한 시점에 하나의 커넥션만 변경할 수 있게 해주는 역할을 한다.
트랜잭션이 없다면 작업 단위의 안정성이 사라져 Partial update와 같은 현상이 발생하고, 정합성이 깨지게 된다.
MyISAM, MEMORY 스토리지 엔진은 트랜잭션을 지원하지 않는 반면, InnoDB 스토리지 엔진은 트랜잭션을 지원한다.
트랜잭션이 없다는 것은 Partial update가 발생한다는 것이고, 그만큼 애플리케이션 단에서 꼼꼼하게 정합성 보장을 해줘야 한다는 것을 의미하기 때문에, 트랜잭션은 복잡해 보이지만 오히려 고마운 기능이다.
트랜잭션은 최소 범위에 적용하는 것이 좋다.
데이터베이스 커넥션은 개수가 제한적이기 때문에, 각 단위 작업이 커넥션을 소유하는 시간이 길어질수록 사용 가능한 커넥션의 수가 줄어든다.
이는 커넥션 획득 지연을 발생시켜 웹 서버, DBMS 서버 모두를 위험하게 만들 수 있다.
잠금은 크게 MySQL 엔진 레벨과 스토리지 엔진 레벨로 나눌 수 있다.
MySQL 엔진 레벨의 잠금은 모든 스토리지 엔진에 영향을 미치지만, 스토리지 엔틴 레벨의 잠금은 스토리지 엔진 간 상호 영향을 주지는 않는다.
글로벌 락
테이블 락
네임드 락
메타데이터 락
InnoDB 스토리지 엔진은 MySQL에서 제공하는 잠금과 별개로 레코드 기반의 잠금 방식을 가지고 있어서, MyISAM보다 더 뛰어난 동시성 처리를 제공한다.
하지만 이원화된 잠금으로 인해 이전 버전에서는 MySQL 명령을 이용해 잠금 정보를 조회하기가 쉽지 않았다.
그러나 5.1 버전에서는 information_schema
데이터베이스의 INNODB_TRX
, INNODB_LOCKS
, INNODB_LOCK_WAITS
라는 테이블을 조인해서 조회하면 현재 어떤 트랜잭션이 잠금을 대기하고 있고 해당 잠금을 어느 트랜잭션이 가지고 있는지 확인할 수 있다.
또한 장시간 잠금을 가지고 있는 클라이언트를 찾아 종료시킬 수도 있다.
레코드 락
갭 락
넥스트 키 락
InnoDB의 갭 락이나 넥스트 키 락은 바이너리 로그에 기록되는 쿼리가 레플리카 서버에서 실행될 때 소스 서버에서 만들어 낸 결과와 동일한 결과를 만들어내도록 보장하는 것이 주목적이다. 그런데 의외로 해당 락으로 인해 데드락이 발생하거나 다른 트랜잭션을 기다리게 만드는 일이 자주 발생하므로, 가능하다면 바이너리 로그 포맷을 ROW 형태로 바꿔서 해당 락을 줄이는 것이 좋다.
자동 증가 락
MySQL 5.1 이상부터는 innodb_autoinc_lock_mode
라는 시스템 변수를 이용해 자동 증가 락의 작동 방식을 변경할 수 있다.
innodb_autoinc_lock_mode=0
: MySQL 5.0과 동일한 잠금 방식으로 모든 INSERT 문장은 자동 증가 락을 사용한다.innodb_autoinc_lock_mode=1
: INSERT되는 레코드의 건수를 정확히 예측할 수 있을 때는 자동 증가 락을 사용하지 않고, 훨씬 가볍고 빠른 래치(뮤텍스)를 이용해 처리한다.innodb_autoinc_lock_mode=2
: 항상 래치(뮤텍스)를 이용해 처리한다. MySQL 8.0의 바이너리 로그 포맷이 기본 ROW 포맷으로 변경되면서 해당 설정도 2가 기본값이 되었다.InnoDB의 잠금은 레코드를 잠그는 것이 아니라 인덱스를 잠그기 때문에, UPDATE 문 등에서 적절한 인덱스가 없다면 테이블을 풀 스캔하면서 모든 레코드를 잠글 수도 있으니 주의해야 한다.
MySQL 5.1부터는 information_schema
데이터베이스의 INNODB_TRX
, INNODB_LOCKS
, INNODB_LOCK_WAITS
라는 테이블을 사용해 잠금을 확인할 수 있었는데, MySQL 8.0부터는 information_schema
의 정보들은 조금씩 제거되고 있으며, 대신 performance_schema
의 data_locks
와 data_lock_waits
테이블로 대체되고 있다.
격리 수준(isolation level)이란 여러 트랜잭션이 동시에 처리될 때, 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있게 허용할지 말지를 결정하는 것이다.
READ UNCOMMITTED (DIRTY READ)
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
뒤로 갈수록 각 트랜잭션 간의 데이터 격리(고립) 정도가 높아지며, 동시 처리 성능도 떨어지는 것이 일반적이다.