트랜잭션과 잠금

이경환·2023년 12월 4일

DB

목록 보기
7/7

Reql MySQL1권 트랜잭션과 잠금 부분을 학습 하며 정리한 페이지입니다.

트랜잭션 과 잠금(Lock) 차이점

잠금은 동시성을 제어하기 위한 기능이고 트랜잭션은 데이터의 정합성을 보장하기 위한 기능이다.

하나의 회원 정보 레코드를 여러 커넥션에서 동 시에 변경하려고 하는데 잠금이 없다면 하나의 데이터를 여러 커넥션에서 동시에 변경할수 있게 된다. 결과적으로 해당 레코드의 값은 예측할 수 없는 상태가 된다. 즉 잠금은 여러 커넥션에서 동시에 동일한 자원(레코드나 테이블)을 요청할 경우 순서대로 한 시점에는 하나의 커넥션만 변경할 수 있게 해주는 역할을 한다. -책 내용-

잠금(Lock): 잠금은 여러 커넥션에서 동시에 동일한 자원(레코드, 테이블)을 요청할 경우 한 시점에는 하나의 커넥션만 해당 자원을 변경할 수 있게 해주는 역할을 한다. 동시성 제어를 위한 기능이다.

격리 수준이라는 것은 하나의 트랜잭션 내에서 또는 여러 트랜잭션 간의 작업 내용을 어떻게 공유하고 차단할 것 인지를 결정하는 레벨을 의미합니다.

트랜잭션(Transaction):트랜잭션은 작업의 완전성을 보장해 주는 것이다. 즉 논리적인 작업 셋을 모두 완벽하게 처리하거나, 처리하지 못할 경우에는 원 상태로 복구해서 작업의 일부만 적용되는 현상 Partial Update (부분 업데이트)이 발생하지 않게 만들어주는 기능이다.

트랜잭션(Transaction)

정리 해보면 "쪼갤 수 없는 업무 처리의 최소 단위”

작업을 완료했을 때 즉 COMMIT 했을 때 모두 적용이 되거나, 실패 했을 때 모두 실패 되어야 하는 경우를 보장해준다.

트랜잭션 성질 (ACID 성질) 이라는 것도 결국 위의 트랜잭션의 성질들을 4가지로 표현 한 것이다.

Mysql에서 트랜잭션은 특징은?

재밌는 사실은 트랜잭션이 있으면 더 고민이 많을 것 같지만 MylSAM이나 MEMORY이 더 많은 고민거리를 만들어냅니다. 즉 트랜잭션이 복잡해 보이지만 개발자에게 더 큰 편리한 혜택을 제공합니다.

책의 예제에도 나와 있지만 MyISAM 같은 경우에는 트랜잭션이 보장이 안 돼 3이 미리 저장되어 있을 경우 1,2,3을 넣을 경우 1,2 가 insert 되고 비로소 3을 만날 때 에러가 터집니다.

이런 부분적인 업데이트를 Partial Update (부분 업데이트)라 부릅니다.

이런 경우 어떤 작업은 정상적으로 수행되었는지 어떤 작업은 실패했는지 따져 봐야 할 수 있고 데이터를 다시 넣고 싶을 경우 장애가 나기 전까지의 insert 된 데이터를 삭제하고 다시 insert 하거나 하는 고민거리만 늘어날 것입니다.

즉 첫 문단에서 말한 트랜잭션을 사용하지 않음으로써 더 복잡한 상황에 직면합니다. 만약 트랜잭션을 사용했을 경우 (즉 INNODB를 사용해 똑같은 작업을 했을 경우) 트랜잭션의 원칙대로 INSERT 문장을 실행하기 전 상태로 그대로 복구합니다.

트랜잭션 주의점

트랜잭션 범위 최적화

만약 작업의 순서가 밑과 같을 시

로그인(트랜잭션 시작 COMMIT) → 오류 확인 → 파일 확인 및 저장 → 사용자의 입력 내용을 DBMS에 저장

사용자의 입력 내용을 DBMS에 저장 ← 이 부분에서 실제 DBMS에 저장이 된다면 굳이 앞 부분은 트랜잭션으로 묶을 필요가없다. 즉 트랜잭션 시작을 로그인 부분 부터 할 필요가 없다.

실제로 DBMS에 데이터를 저장하는 작업(트랜잭션)이 아니라면 굳이 DBMS의 트랜잭션에 포함시킬 필요는 없습니다. 일반적으로 데이터베이스 커넥션은 개수가 제한적이어서 각 단위 프로그램이 커 넥션을 소유하는 시간이 길어질수록 사용 가능한 여유 커넥션의 개수는 줄어듭니다. 그리고 어느 순간에는 각 단 위 프로그램에서 커넥션을 가져가기 위해 기다려야 하는 상황이 발생할 수도있습니다.

또한 저장된 데이터의 단순 확인 및 조회하는 작업의 경우 트랜잭션에 포함할 필요는 없습니다. 그리 작업의 성격이 다른 경우 함께 묶지 않아도 무방합니다. 이러한 작업은 별도의 트랜잭션으로 분리하는 것이 좋습니다.

메일 전송, FTP 파일 전송 작업 네트워크를 통한 원격 서버는 어떻게든 DBMS의 트랜잭션 내에서 제거하자.

만약 위의 흐름에서 알림 메일 발송이 추가를 고려해 보겠습니다.

로그인 → 오류 확인 → 파일 확인 및 저장 → 사용자의 입력 내용을 DBMS에 저장 → 게시물 등록에 대한 알림 메일 발송 → 알림 메일 발송 이력을 DBMS에 저장(트랜잭션 종료 COMMIT)

만약 프로그램이 실행되는 동안 메일 서버와 통신할 수 없는 상황이 발생한다면 웹 서버뿐 아니라 DBMS 서버까지 위험해지는 상황이 발생할 수 있습니다.

잠금(Lock)

MySQL 서버에서의 잠금은 크게 mysql 엔진 레벨 잠금 과 스토리지 엔진 레벨 잠금으로 구분할 수 있다.

특징

MySQL 엔진 레벨의 잠금은 모든 스토리지 엔진에 영향을 미치지만, 스토리지 엔진 레벨의 잠금은 스토리지 엔진 간 상호 영향을 미치지 않는다.

스토리지 엔진은 MyISAM, InnoDB 를말 하는 것일거고
mysql 엔진은 커넥션 핸들러, sql 파서, 옵티마이저 를 말하는 것 레코드
락을 제외하고는 거의 대부분이 mysql 엔진 락이다.
MyISAM과 MEMORY 엔진은 자체적인 락 기능을 가지고 있지않기 때문이다.
  • MySQL 엔진 레벨
    • 글로벌 락
    • 테이블 락
    • 메타데이터 락
    • 네임드 락 (유저 레벨 락)
  • 스토리지 엔진 레벨(InnoDB)
    • 레코드 락
    • 갭 락
    • 넥스트 키 락
    • 자동 증가 락

MySQL 엔진 레벨 락

MySQL 엔진 레벨 잠금은 크게 글로벌 락, 테이블 락, 네임드 락, 메타데이터 락이 있습니다.

글로벌 락

글로벌 락이 영향을 미치는 범위는 MySQL 서버 전체이며, 작업 대상 테이블 이나 데이터베이스가 다르더라도 동일하게 영향을 미칩니다.

즉 MySQL 서버에 존재하는 모든 테이블에 잠금을 걸게 됩니다.

테이블 락

개별 테이블 단위로 잠금을 거는 방식이며, 명시적 또는 묵시적으로 특정 테이블의 락을 획득할 수 있습니다.

  • 묵시적 방법

MyISAM이나 Memory DB에서 데이터를 변경하는 쿼리를 실행하면 자동으로 테이블 락이 획득 됩니다.

(쿼리가 실행되는 동안 자동으로 획득한 후 쿼리가 완료되면 자동으로 해제됩니다. 참고로 InnoDB의 경우 레코드 기반 잠금을 사용)

  • 명시적 방법 InnoDB도 아래와 같이 명시적으로 선언하여 Table LOCK을 획득할 수 있습니다.
    • Table READ

      다른 트랜잭션에서 READ가 가능하지만 WRITE가 불가능

    • Table WRITE

      락을 건 트랜잭션만이 해당 테이블에 접근 가능하고, 다른 트랜잭션은 접근 불가능하다. read, write를 모두 포함 다른 트랜잭션에서 조회도 불가능하다.

네임드 락

  • 임의의 문자열에 대해 잠금을 설정할 수 있다.
  • 네임드 락은 단순히 사용자가 지정한 문자열(String)에 대해 획득하고 반납(해제)하는 잠금 이다.
  • 네임드 락은 자주 사용되지는 않는다

DB와 서버가 1:N일 때 동시성 문제를 해결할 때 고려해 볼만합니다.

`메타데이터 락 ,네임드 락 도 존재한다.`

스토리지 엔진(InnoDB) 레벨 락

테이블의 데이터를 다루기 위한 락입니다. (Mysql엔진 레벨의 락은 테이블, 데이터 베이스 등과 같은 부분의 락)

레코드락

레코드 락이란 레코드를 잠그기 위한 락인데, MySQL의 경우에는 실제 레코드 자체가 아니라 인덱스를 잠근다는 점에서 다른 DBMS와 큰 차이가 있다. 그리고 레코드를 잠그는 것과 인덱스를 잠그는 것은 상당히 중요한 차이를 만들어 낸다.
락이 걸리는 인덱스는 클러스트 인덱스(pk) 와 논클러스터 인덱스 모두를 포함합니다.

CREATE TABLE IF NOT EXISTS `member`
(
    `id`           bigint          NOT NULL PRIMARY KEY AUTO_INCREMENT,
	  `first_name`   VARCHAR(64),
	  `last_name`    VARCHAR(64),
    `profile`      varchar(2500)   NOT NULL,
		INDEX idx_last_name (last_name)
);
UPDATE 
    member
WHERE 
    last_name = 'Lee'           // 이 조건이 150건
    AND first_name = 'Gamja'    //  이 조건이1건

위와 같은 구조를 가지는 테이블이 있다고 한다면. 위 조건대로 각 컬럼이 150건, 1건씩 존재 한다면 first_name = 'Gamja'이 1건인 데이터를 업데이트 하기 위해서는 150건의 인덱스에 락을 걸어야 합니다.

만약 last_name인덱스 조차 없는 경우에는 UPDATE 쿼리를 위해서는 table full scan하며 레코드를 갱신해야 합니다. 이때 모든 테이블의 모든 레코드를 락 하게 됩니다.

즉 mysql에서 데이터를 수정하는 경우에는 인덱스나 PK를 신중하게 설계 해주어야 table full scan 이나 불 필요한 락을 피할 수 있게 됩니다.

넥스트 키 락

레코드 락과 갭 락을 합쳐 놓은 형태의 잠금을 넥스트 키 락이라 합니다.

자동 증가 락(Auto incremnet lock)

  • 중복되지 않고 순차적으로 증가하는 일련번호를 제공하기 위해 내부적으로 테이블 수준의 잠금인 자동 증가 락을 사용합니다.
  • INSERT, REPLACE와 같은 새로운 레코드를 저장하는 쿼리에서만 사용됩니다.
  • 트랜잭션과 관계없이 문장에서 AUTO_INCREMENT 값을 가져오는 순간 락이 걸립니다.
  • 자동 증가 락은 테이블에 1개만 존재 하기 때문에 한 쿼리에서 락을 획득중이라면 다음 쿼리는 락을 대기합니다.
  • 자동 증가 락은 잠금을 최소화 하기 위해 한 번 증가하면 절대 자동으로 줄어들지 않습니다. 즉 트랜잭션이 롤백되어도 자동으로 값이 복구되지않고 그대로 남습니다.
profile
개선하는 개발자, 이경환입니다

0개의 댓글