최근 노션으로만 정리를하다 보니 velog 이용을 하지 않게 됐다..오랜만에 velog를 통해서 정리하고자 한다.
MySQL 스터디를 진행하면서 DB에 대해 공부하면서 재밌게 배워가고 있다. 이번에는 DB 인덱스에 대해 스터디하기로 했는데, 기대가 된다!...일단 이전에 배웠던 부분을 정리하겠다
개별 테이블 단위로 설정되는 lock으로 명시적 또는 묵시적으로 특정 테이블의 락을 획득할 수 있다. 명시적으로는 명령을 통해 특정 테이블의 락을 획득할 수 있다. 묵시적인 테이블락은 테이블의 데이터를 변경하는 쿼리를 실행하면 발생한다. MySQL 서버가 데이터가 변경되는 테이블에 잠금을 설정하고 데이터를 변경한 후, 즉시 잠금을 해제하는 형태로 사용된다. 즉 묵시적인 테이블 락은 쿼리가 실행되는 동안 자동으로 획득했다가 쿼리가 완료된 후 자동 해제된다!
하지만, InnoDB 테이블의 경우 스토리지 엔진 차원에서 레코드 기반의 잠금을 제공하기 때문에 단순 데이터 변경 쿼리로 인해 묵시적인 테이블 락이 설정되지는 않는다. 더 정확히는 InnoDB 테이블에도 테이블 락이 설정되지만 대부분 데이터변경(DML) 쿼리에서는 무시되고 스키마를 변경하는 쿼리(DDL)의 경우에만 영향을 미친다.
네임드 락은 GET_LOCK() 함수를 이용해 임의의 문자열에 대해 잠금을 설정할 수 있다. 이 잠금의 특징은 대상이 테이블이나 레코드 또는 AUTO_INCREMENT와 같은 데이터베이스 객체가 아니라는 것이다! 네임드 락은 단순히 사용자가 지정한 문자열(String)에 대해 획득하고 반납하는 lock이다
네임드락은 여러 클라이언트가 상호 동기화를 처리해야 할 때 네임드 락을 이용하면 쉽게 해결할 수 있다!
ex) 정산진행시
// "mylock"이라는 문자열에 대해 잠금을 획득한다.
// 이미 잠금을 사용 중이면 2초 동안만 대기한다. (2초 이후 잠금 자동 해제)
SELECT GET_LOCK('mylock',2);
// "mylock"이라는 문자열에 대해 잠금이 설정돼 있는지 확인한다.
SELECT IS_FREE_LOCK('mylock');
// "mylock"이라는 문자열에 대해 획득했던 잠금을 반납(해제)한다.
SELECT RELEASE_LOCK('mylock');
// 3개 함수 모두 정상적으로 락을 획득하거나 해제한 경우에는 1을,
// 아니면 NULL이나 0을 반환한다.
메타데이터 락(Metadata Lock)은 데이터베이스 객체(대표적으로 테이블이나 뷰 등)의 이름이나 구조를 변경하는 경우에 획득하는 잠금이다. 메타데이터 락은 명시적으로 획득하거나 해제할 수 없다!
"RENAME TABLE tab_a T0 tab_B"같이 테이블의 이름을 변경하는 경우 자동으로 획득하는 잠금이다.
이건 아직 이해가 잘안된다...
데이터베이스에서 비관적락은 보통 레코드 또는 테이블에 대한 잠금(lock)을 사용하는 경우 발생한다. 즉, 한 트랜잭션이 데이터를 수정하는 동안 다른 트랜잭션은 같은 데이터를 읽거나 수정할 수 없다. 이는 트랜잭션 일관성을 보장하고 데이터 무결성을 유지하기 위한 것이다
비관적락은 트랜잭션 충돌을 방지하는 데 효과적이지만, 동시에 성능 저하를 초래할 수 있다. 특히, 한 트랜잭션이 자원을 점유하는 동안 다른 트랜잭션이 해당 자원에 대한 접근을 막기 때문이다!!!
InnoDB 스토리지 엔진은 MySQL에서 제공하는 잠금과는 별개로 스토리지 엔진 내부에서 레코드 기반의 잠금 방식을 탑재하고 있다. InnoDB는 레코드 기반의 잠금 방식 때문에 MyISAM보다 훨씬 뛰어난 동시성 처리를 제공할 수 있다.
InnoDB의 트랜잭션과 잠금, 그리고 잠금 대기 중인 트랜잭션의 목록을 조회할 수 있는 방법이 도입 됐다. MySQL서버의 information_schema 데이터 베이스에 존재하는 INNODB_TRX, INNODB_LOCKS, INNODB_LOCK_WAITS라는 테이블을 조인해서 조회하면 현재 어떤 트랜잭션이 어떤 잠금을 대기하고 있고 해당 잠금을 어느 트랜잭션이 가지고 있는지 확인할 수 있으며, 또한 장시간 잠금을 가지고 있는 클라이언트를 찾아서 종료시킬 수도 있다.
InnoDB 스토리지 엔진은 레코드 기반의 잠금 기능
을 제공하며, 잠금 정보가 상당히 작은 공간으로 관리되기 때문에 레코드 락이 페이지 락으로, 또는 테이블 락으로 레벨업되는 경우는 없다.
레코드 자체만을 잠그는 것을 레코드 락(Record lock, Record only lock)이라고 하며, 다른 사용 DMBS의 레코드 락과 동일한 역할을 한다. 한 가지 중요한 차이는 InnoDB 스토리지 엔진은 레코드 자체가 아니라 인덱스의 레코드를 잠근다는 점이다. 인덱스가 하나도 없는 테이블이더라도 내부적으로 자동 생성된 클러스터 인덱스를 이용해 잠금을 설정한다. 많은 사용자가 간과하는 부분이지만 레코드 자체를 잠그느냐, 아니면 인덱스를 잠그느냐는 상당히 크고 중요한 차이를 만들어 낸다!
다른 DBMS와의 또 다른 차이가 바로 갭 락(Gap lock)이다. 갭 락은 레코드 자체가 아니라 레코드와 바로 인접한 레코드 사이의 간격만을 잠그는 것을 의미한다. 갭 락의 역할은 레코드와 레코드 사이의 간격에 새로운 레코드가 생성(INSERT)되는 것을 제어하는 것이다. 갭 락은 그 자체보다는 이어서 설명할 넥스트 키 락의 일부로 자주 사용된다.
레코드 락과 갭락을 합쳐 놓은 형태의 잠금을 넥스트 키 락(Next key lock)이라고 한다. STATEMENT 포맷의 바이너리 로그를 사용하는 MySQL 서버에서는 REPEATABLE READ 격리 수준을 사용해야한다. 또한 innodb_locks_unsafe_for_beginlog 시스템 변수가 비활성화되면 변경을 위해 검색하는 레코드에는 넥스트 키 락 방식으로 잠금이 걸린다. InnoDB의 갭락이나 넥스트 키 락은 바이너리 로그에 기록되는 쿼리가 레플리카 서버에서 실행될 때 소스 서버에서 만들어 낸 결과와 동일한 결과를 만들어내도록 보장하는 것이 주목적이다.
MySQL에서는 자동 증가하는 숫자 값을 추출하기 위해 AUTO_INCREMENT라는 컬럼 속성을 제공한다. AUTO_INCREMENT 컬럼이 사용된 테이블에 동시에 여러 레코드가 INSERT되는 경우, 저장되는 각 레코드는 중복되지 않고 저장된 순서대로 증가하는 일련번호 값을 가져야한다. InnoDB 스토리지 엔진에서는 이를 위해 내부적으로 AUTO_INCREMENT락 이라고 하는 테이블 수준의 잠금을 사용한다.
AUTO_INCREMENT락은 INSERT와 REPLACE 쿼리 문장과 같이 새로운 레코드를 저장하는 쿼리에서만 필요하며, UPDATE나 DELETE 등의 쿼리에서는 걸리지 않는다. InnoDB의 다른 잠금과는 달리 AUTO_INCREMENT락은 트랜잭션과 관계없이 INSERT나 REPLACE 문장에서 AUTO_INCREMENT값을 가져오는 순간만 락이 걸렸다가 즉시 해제된다. AUTO_INCREMENT락은 테이블에 단 하나만 존재하기 때문에 두 개의 INSERT 쿼리가 동시에 실행되는 경우 하나의 쿼리가 AUTO_INCREMENT 락을 걸면 나머지 쿼리는 AUTO_INCREMENT 락을 기다려야 한다.
자동 증가 값이 한 번 증가하면 절대 줄어들지 않는 이유가 AUTO_INCREMENT 잠금을 최소화하기 위해서다 설령 INSERT 쿼리가 실패했더라도 한 번 증가된 AUTO_INCREMENT 값은 다시 줄어들지 않고 그대로 남는다.