이론 정리/Database

Real MySQL 5장 정리

철매존 2023. 12. 3. 01:09
728x90

트랜잭션과 락

  • 트랜잭션
    • 작업의 완전성을 보장
      • 데이터의 정합성 보장
  • 잠금
    • 동시성 제어 기능

트랜잭션

여러 논리작업이 한꺼번에(다같이 되거나 다같이 안되거나) 진행되도록 하는것이다.

그리고 트랜잭션은 무조건 모든곳에 넣는것은 좋지 않은데 예를 들어 유저가 글을 쓰는 로직을 볼 때

  1. 유저 정보 가져옴
  2. 글쓰기 오류 판별
  3. 업로드 파일 확인 및 저장
  4. 사용자 입력 정보 DBMS 저장
  5. 저장 내용을 DBMS에서 조회
  6. 게시물 등록 알림을 메일로 전송
  7. 알림 메일 발송 이력을 DBMS 저장

이렇게 보면
3 ~ 4가 실제 DB에 글을 저장하는 로직
7이 DB에 글을 저장하는 로직이다.
그리고 다른 부분은 이를 활용하는 것이거나 '6'번처럼 네트워크를 써서 문제가 생기는 경우가 있을 것이다.

  • 처음부터 트랜잭션을 만들지 말고 필요할 때에 구성하여 사용하는것이 좋다.

잠금

  • 스토리지 엔진 레벨 락
  • MySQL 락

글로벌 락

  • MySQL 락 중 가장 범위가 큰 락
  • MySQL 서버 전체에 영향을 준다.
  • 당연한거지만 락이 걸려있으면 다른 명령을 쓰지 못한다.

백업 락

  • 위의 글로벌 락의 경우는 서버의 모든 변경 작업을 멈춘다.
  • InnoDB에서 트랜잭션을 지원하기 때문에 데이터 상태를 위해 모든 변경 작업을 멈출 필요는 없다.
  • 백업 툴들의 안정적인 실행을 위해 백업 락 도입됨.
  • 특정 세션에서 백업락 획득하면 모든 세션에서 테이블의 스키마나 사용자의 인증 관련 정보를 변경할 수 없다.
  • 일반적인 테이블의 데이터 변경은 허용된다.

테이블 락

  • 개별 테이블 단위 잠금
  • 명시적, 묵시적 락이 있는데(명시적 락은 명시적으로 해제 가능) 명시적 락은 특별한 경우가 아니면 잘 안쓰임
    • 묵시적 락은 MyISAM이나 MEMORY테이블에 데이터를 변경하는 쿼리를 실행하면 발생
      • 변경할때 잠금 -> 변경 즉시 해제
      • 근데 InnoDB의 경우는 스토리지 차원에서 레코드 기반 잠금을 제공해서 테이블 락이 걸리지 않음
        • DDL의 경우만 영향을 미침

네임드 락

  • GET_LOCK함수를 통해 임의의 문자열에 대해 잠금 설정 가능
  • 대상이 테이블이나레코드 또는 데이터베이스 객체가 아니다.
    • 사용자가 지정한 문자열에 대해 획득하고 반납하는 잠금
  • 여러 클라이언트가 어떤 정보를 동기화하는 것처럼 상호 동기화 처리해야 할 때 요긴함
  • 배치처럼 한번에 많은 레코드를 변경하는 쿼리는 데드락의 원인이 되고는 하는데 동일 데이터를 변경하거나 참조하는 프로그램끼리 분류해서 네임드 락을 걸면 해결이 쉽다

메타데이터 락

  • DB객체(테이블이나 뷰 등)의 이름이나 구조를 변경하는 경우에 획득
    • 얘는 자동 획득(명시적 획득 불가)
  • Rename같은 경우 원본 이름이랑바꿀 이름 둘다 잠금을 건다.

InnoDB 스토리지 엔진 잠금

얘는 레코드 기반 잠금 기능을 제공한다.
락 에스컬레이션은 없다.

InnoDB에서는 레코드 끼리의 간격을 잠그는 갭 락이라는 것도 있다.

레코드 락

  • 레코드를 잠그는 것으로 다른 상용 DBMS의 레코드 락이랑 동일한 역할을 한다.
  • 한가지 차이는 InnoDB에서는 인덱스의 레코드를 잠금
    • 인덱스가 없어도, 자동 생성된 클러스터 인덱스를 이용해서 잠금을 실행한다.
  • 보조 인덱스를 이용한 변경 -> 넥스트 키 락이나 갭 락 사용
  • PK나 UK에 의한 변경 작업에서는 레코드 자체에만 락을 건다.

갭 락

  • 레코드와 바로 인접한 레코드 사이의 간격만들 잠그는것
  • 이거 역할은 저 레코드 사이에 새로운 레코드가 생성되는거를 제어하는것이다.
  • 이거는 뒤에서 나오는 넥스트 키 락의 일부로 사용됨

넥스트 키 락

  • 레코드락 + 갭락
  • InnoDB의 갭락이나 넥스트 키 락은 바이너리 로그에 기록되는 쿼리가 레플리카 서버에서 실행될 때 소스 서버에서 만들어 낸 결과와 동일한 결과를 만들어내도록 보장하는 것이 주목적

자동 증가 락

  • MySQL에서는 자동증가하는 숫자값을 추출하기 위해 AUTOO_INCREMENT 속성을 제공한다.
  • 이게 여러 레코드가 insert되면 서로 중복없이 저장된 순서대로 증가해야 하는데 이를 자동 증가 락이라 하는 테이블 수준 잠금을 사용함
  • 참고로 자동 증가 값이 한번 증가하면 다시 줄어들지 않는 이유는 자동 증가 락의 잠금을 최소화하기 때문이다.

인덱스와 잠금

InnoDB의 잠금과 인덱스는 상당히 중요한 연관 관계가 있다.

  • InnoDB의 잠금은 레코드가 아니라 인덱스를 잠그는 방식으로 처리된다.
    • 변경해야 할 레코드를 찾기 위해 검색한 인덱스의 레코드를 모두 락을 걸아야 한다는 것
      • 그래서 UPDATE작업을 하거나 할 때에 인덱스 설계가 매우매우매우매우 중요하다!!!

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

  • InnoDB의 레코드 수준 잠금은 테이블 수준 잠금보다는 조금 더 복잡한데, 레코드에 잠금이 걸리고 그게 오랫동안 잠겨져 있어도 잘 발견이 안된다.
  • 요즘은(5.1버전 이상) 조회가 가능함 ㅇㅇ.

MySQL의 격리 수준

사실 이전에 한번 공부하기는 했는데 다시 봐보자

Dirty Read Non-Repeatable Read Phantom Read
Read Uncommitted 발생 발생 발생
Read Committed 없음 발생 발생
Repeatable Read 없음 없음 발생(InnoDB는 없음)
Serializable 없음 없음 없음

Read Uncommitted

  • 각 트랜잭션 변경 내용이 commit이나 rollback여부와 관계 없이 보임
  • Dirty Read 발생 가능
    • 데이터가 롤백되었는데 그게 마치 있는 것처럼 보이는 현상
  • 참고로 이 Read Uncommitted는 정합성에 많은 문제가 있어 아예 격리 수준으로 인정하지도 않는다고 한다.(그럴만함)

Read Committed

  • 오라클 기본 격리수준 + 온라인 서비스에서 가장 많이 선택되는 격리 수준
  • 이거는 Commit 완료된 데이터만 조회 가능하다.
  • Non-Repeatable Read 발생 가능
    • 한 트랜잭션 내에서 다른 트랜잭션의 커밋 시점에 의해 다른 결과를 가져오게 된다.
    • 즉 Repeatable Read 정합성에 어긋난다.
    • 금전처리처럼 하나의 트랜잭션에서 동일 데이터를 여러번 읽고 작업하는 경우 문제가 된다.

Repeatable Read

  • MySQL의 InnoDB 스토리지 엔진에서 기본으로 사용
  • 바이너리 로그를 가진 MySQL서버에서는 최소 Repeatable Read 격리 수준 이상을 사용해야 한다.
  • 이거는 Non-Repeatable Read 부정합이 발생하지 않음
  • 트랜잭션 Rollback에 대비해서 언두로그 백업하고 그 데이터를 이용해 동일 트랜잭션 내에서는 동일한 데이터를 보여준다.
  • 트랜잭션이 각각의 트랜잭션 번호를 가져서(이게 순차적 증가임) 언두 영역 레코드에는 이 번호가 저장되어 있고 그거를 통해 데이터를 가져오면 되는것
  • 이것도 근데 Phantom Read 현상 발생 가능
    • select for update같은 경우는 select대상 레코드에 쓰기 잠금을 걸어야 하는데 언두 레코드에는 잠금을 걸 수 없다.
    • 그래서 이거는 언두영역의 변경 전 데이터가 아니라 현재 레코드의 값을 가져오는것

Serializable

  • 가장 단순하고 엄격하다.
  • 동시 처리 성능도 떨어짐
  • 읽기 잠금에도 공유잠금을 걸어주는거
    • 참고로 InnoDB에서 "Non-locking consistent read(잠금이 필요없는 일관된 읽기)"라는 말이 존재하는데, 얘는 본래 보통은 읽기에 잠금을 걸지 않는다는 것이다.
      • 그래서 이거 락 걸어주면 성능이 떨어진다.
  • 근데 InnoDB스토리지 엔진에서는 갭락과 넥스트 키 락 덕분에 Repeatable Read에서도 Phantom Read가 발생하지 않는다(select for update, select for share처럼 잠금을 동반한 select는 예외)
    • 지금 트랜잭션에서 읽는 대상이 된 인덱스에 대해서는 데이터의 입력이 불가능하므로...
    • 그러니까 그냥 InnoDB에서는 Repeatable Read를 쓰면 되는데 이걸 굳이 쓸 필요는 없다는 것