1장에 이어서

1장 MySQL 구조 (1)

InnoDB 구조


버퍼풀과 리두 로그

  • InnoDB의 버퍼풀과 리두로그는 매우 밀접한 관계를 맺고 있다.
  • InnoDB의 버퍼풀은 클린 페이지와 더티페이지 이 두가지의 페이지를 가지고 있다.
    • 클린 페이지: 디스크에서 읽은 상태로 변경이 전혀 없는 페이지.
    • 더티 페이지: Insert, Update, Delete 명령으로 변경된 데이터를 가진 페이지.
      • 더티 페이지는 디스크와 버퍼의 상태가 서로 다르기 때문에 언젠간 디스크에 기록/갱신 되어야 한다. 더티 페이지에 무한정 머물수 없기 때문에 1개이상의 고정 크기 파일을 연결해서 순환고리처럼 사용한다.
      • 즉 데이터 변경이 계속 발생하면 리두로그 파일에 기록됐던 로그 엔트리는 어느 순간 새로운 로그엔트리로 덮어 쓰인다.
      • 그래서 InnoDB 스토리지 엔진은 전체 리두 로그파일에서 재사용 가능한 공간과 재사용 불가능한 공간을 구분해서 관리해야 하는데, 불가능한 공간을 Active Redo Log 라고 한다.
  • 리두 로그 파일의 공간은 계속 순환되어 재사용되며 매번 기록될 때마다 로그의 포지션은 계속 증가된 값을 갖게 된다. 이를 Log Sequence Number라 한다. (LSN)
  • 가장 최근 체크포인트의 LSN과 마지막 리두로그 엔트리의 LSN의 차이를 체크포인트 에이지 라고한다. 즉, 이 체크 포인트 에이지ActiveRedoLog의 공간의 크기가 된다.

버퍼풀 플러시

  • InnoDB 엔진은 버퍼풀에서 디스크로 기록되지 않은 더티 페이지들을 성능상의 악영향없이 디스크에 동기화하기 위해 다음과 같이 2개의 플러시 기능을 백그라운드에서 실행한다.
    • 플러시 리스트 플러시
    • LRU 리스트 플러시

플러시 리스트 플러시

  • 리두 로그 공간의 재활용을 위해 주기적으로 오래된 리두 로그 엔트리의 공간을 비워야 한다. 그런데 이때 오래된 리두 로그 공간이 지워지려면 InnoDB 버퍼풀의 더티페이지가 먼저 디스크로 동기화되어야 한다. 이를 위해 InnoDB 엔진은 주기적으로 플러시 리스트 플러시 함수를 호출해서 동기화 작업을 수행한다.

LRU 리스트 플러시

  • LRU 리스트에서 사용 빈도가 낮은 데이터 페이지들을 제거해서 새로운 페이지들을 읽어올 공간을 확보한다.
  • 플러시를 수행하기위해 스캔작업을 하는데 스캔하면서 더티 페이지는 디스크에 동기화되며, 클린 페이지는 Free리스트 로 페이지를 이동시킨다.

DoubleWriteBuffer

  • InnoDB 스토리지 엔진에서 더티페이지를 디스크로 플러시할때 일부만 기록되는 문제(하드웨어 오작동 혹은 시스템 비정상 종료 등)가 발생할 가능성이 있는데 이렇게 페이지가 일부만 기록되는 현상을 Partial page/Torn page 라고 한다. 이를 방지 하기 위해 Double Write 기법을 사용한다.
    • Double Write 기법은 InnoDB 스토리지 엔진이 실제 데이터 파일에 변경을 기록하기 전에 먼저 더티 페이지를 묶어서 한번의 디스크 쓰기로 DoubleWrite 버퍼에 기록한다. 그리고 각 더티 페이지를 파일의 적당한 위치에 하나씩 랜덤으로 쓰기를 실행한다.
    • 예를들어 A ~ C 페이지를 기록하는 중 비정상적으로 종료되어 C페이지 가 기록이 되지 않았다고 가정해보자. 그러면 InnoDB 엔진은 재시작될 때 항상 DoubleWrite 버퍼의 내용과 데이터 파일의 페이지들을 모두 비교해서 다른 내용을 담고 있는 페이지가 있으면 버퍼의 내용을 데이터 파일의 페이지로 복사한다.

언두 로그 (Undo Logs)

  • InnoDB 엔진은 트랜잭션과 격리수준을 보장하기 위해 Insert, Update, Delete로 변경되기 이전 버전의 데이터를 별도로 백업해놓는데, 이를 Undo Log라 한다.

  • 언두 로그는 아래의 두가지 상황에 사용된다.

    • 트랜잭션 보장
      • 트랜잭션이 롤백되면 트랜잭션 도중 변경된 데이터를 변경 이전으로 복구할 때 사용한다.
    • 격리 수준 보장
      • 특정 커넥션에서 데이터를 변경하는 중에 다른 커넥션에서 데이터를 조회하면 격리 수준에 맞게 변경 중인 레코드를 읽지 않고 언두 로그에 있는 백업해둔 데이터를 반환하기도 한다.

언두 테이블 스페이스

  • 언두 로그가 저장되는 공간을 언두 테이블 스페이스라 한다.
  • 과거 5.6 이전는 ibdata.ibd 시스템 테이블 스페이스 저장이 되었고, 5.6버전에서는 innodb_undo_tablepsaces 시스템 변수가 도입되었으며,
    8.0 버전부터는 innodb_undo_tablepsacesDeprecated되어 더이상 효력이 없어졌다.
    8.0 부터는 시스템 테이블 스페이스 외부의 별도 로그 파일에 기록되도록 개선되었다.

언두 테이블 스페이스 공간

  • 언두 테이블 스페이스는 1개 ~ 128개 이하의 롤백 세그먼트를 가지며, 롤백 세그먼트는 1개이상의 언두 슬롯을 가진다.

체인지 버퍼 (Change Buffer)

  • 데이터가 Insert 되거나 Update될 때는 데이터 파일을 변경하는 작업뿐만 아니라 해당 테이블의 인덱스 정보를 업데이트하는 작업도 필요하다. 그런데 인덱스를 업데이트하는 작업은 랜덤으로 디스크를 읽은 작업이므로 테이블에 인덱스가 많다면 상당히 많은 자원을 소모하게 된다. 그래서 InnoDB는 변경해야 할 인덱스 페이지가 버퍼풀에 있으면 바로 업데이트를 수행하지만 그렇지 않을 경우 (디스크에서 직접 읽어와서 업데이트해야할 경우) 이를 즉시 실행하지 않고 버퍼에 저장해두고 사용자에게 결과를 반환하는데 이 버퍼를 Change Buffer라 한다.

어댑티브 해시 인덱스

  • InnoDB 엔진에서 사용자가 자주 요청하는 데이터에 대해 자동으로 생성하는 인덱스이다.
  • innodb_adaptive_hash_index 시스템 변수를 이용해서 활성/비활성 할 수 있다.
  • 인덱스 키값과 해당 인덱스 키 값이 저장된 데이터 페이지 주소의 쌍으로 관리되는데, 인덱스 키값은 B-Tree index의 IDB-Tree. index의 실제 키 값 조합으로 생성된다.
  • 즉, 모든 B-Tree 인덱스에 대한 어댑티브 해시 인덱스하나의 해시 인덱스에 저장된다.
  • 데이터 페이지 주소는 실제 키값이 저장된 데이터 페이지의 메모리 주소를 가지는데, 이는 InnoDB 버퍼풀에 로딩된 페이지의 주소를 의미한다. 그래서 어댑티브 해시 인덱스는 버퍼풀에 올려진 데이터 페이지에 대해서만 관리되며, 버퍼풀에서 페이지가 사라진다면 어댑티브 해시인덱스에서도 해당 페이지는 사라진다.