InnoDB 핵심을 이해하기 위한 여행 시작 에서, InnoDB 내부 구조 문서화를 위한 innodb_diagrams 프로젝트를 소개했습니다. 이 포스트의 다이어그램들은 innodb_diagrams
를 이용해 그렸습니다.
Space 및 각 페이지의 기본적인 구조는 Space 파일 레이아웃 기초 에서 설명했습니다. 이제 페이지 및 extent 관리, “여유 공간 (free space)” 관리와 관련된 InnoDB의 구조 및 다양한 목적으로 할당된 페이지들을 추적하는 방법에 대해 설명하겠습니다.
이전에 설명했듯이, InnoDB 페이지는 일반적으로 16 KiB이고, 64개의 연속된 페이지인 1 MiB 블록들로 그룹화되는데, 이를 “extent” 라고 부릅니다. InnoDB는 space 내 고정된 위치에 FSP_HDR 및 XDES 페이지를 할당하여 사용 중인 extent와 사용 중인 extent 내의 페이지를 추적합니다. 이 페이지들은 매우 간단한 구조를 가지고 있습니다:
여기에는 일반적인 FIL 헤더와 트레일러, FSP 헤더 (나중에 설명) 및 256 개의 “extent descriptor” 또는 단순히 “descriptor” 가 포함됩니다. 또한 상당한 양의 미사용 공간이 포함되어 있습니다.
Extent descriptor의 구조는 다음과 같습니다:
Extent descriptor 내 각 필드의 목적은 아래와 같습니다:
Extent를 참조하는 다른 구조체들은 extent의 descriptor가 있는 FSP_HDR 또는 XDES 페이지의 페이지 번호와 descriptor 항목 자체의 해당 페이지 내의 바이트 오프셋을 조합하여 사용합니다. 예를 들어, “page 0 offset 150”이 참조하는 extent는 space의 첫 번째 범위이며 0-63 페이지를 차지하고 “page 16384 offset 270”은 16576-16639 페이지를 차지합니다.
리스트 (InnoDB에서는 “free list”라고 부름)는 여러 관련 구조를 함께 연결할 수 있는 일반적인 구조체입니다. 리스트는 두 개의 상호 보완적인 구조로 구성되어 있으며, 이는 디스크 상의 이중 연결 리스트를 제공합니다. “list base node”는 다음과 같은 구조를 갖습니다:
Base node는 몇몇 하이 레벨 구조 (FSP 헤더와 같은)에서 오직 한번만 저장됩니다. Base node는 리스트의 길이 및 리스트의 처음과 마지막 리스트 노드를 포함하고 있습니다. 실제 “list node”도 비슷한 구조를 갖습니다:
첫 번째와 마지막 노드에 대한 포인터를 저장하는 대신, 리스트 노드는 이전과 다음 노드에 대한 포인터를 저장합니다.
모든 포인터는 페이지 번호 (같은 space 내에 있어야 함)와 리스트 노드를 찾을 수 있는 해당 페이지 내의 byte offest으로 구성됩니다. 모든 포인터는 리스트 노드의 시작 (즉, N+0)을 가리키며, 반드시 서로 연결된 구조여야 하는 건 아닙니다. 예를 들어, extent descriptor 항목이 리스트에 연결된 경우 리스트 노드가 XDES 항목 구조 내에서 offset 8에 있기 때문에, 리스트 항목을 읽는 코드는 descriptor 구조가 리스트 노드 offset의 8 바이트 전에 시작한다는 것을 “알아야” 거기에서 그 구조체를 읽을 수 있습니다. (리스트 노드가 구조체에서 첫 번째로 등장하는 항목인지 항상 확인하는 것이 좋을 것입니다.)
Extent descriptor 항목 자체를 저장하는 것 외에도, FSP_HDR 페이지 (space에서 항상 페이지 0 임)는 많은 리스트들을 포함하는 FSP 헤더도 저장하므로 이전에 쉽게 설명 할 수 없었습니다. FSP 헤더의 구조는 다음과 같습니다:
FSP 헤더에서 리스트와 관련이 없는 필드들의 용도는 아래와 같습니다 (순서대로 아님):
아래의 extent descriptor 리스트에 대한 list base node도 FSP 헤더에 저장됩니다:
파일 segment와 inode는 용어부터 시작해서 아마 InnoDB에서 문서화가 가장 안 된 부분 중 하나일 것입니다. InnoDB는 파일 시스템에서 일반적으로 사용되는 “inode” 라는 용어를 오버로드하고 INODE 항목 (하나의 작은 구조체)과 INODE 페이지 (많은 INODE 항목을 보유한 페이지 타입)에 모두 사용합니다. InnoDB의 INODE 항목은 단순히 파일 segment (FSEG)를 의미하며, 이제부터는 “파일 segment INODE” 라고 합니다. INODE 페이지의 구조는 다음과 같습니다:
각 INODE 페이지에는 85개의 파일 segment INODE 항목 (16KiB 페이지)이 포함되어 있으며 각각 192 bytes입니다. 또한 여기에는 위에서 설명한 FSP_HDR의 FSP 헤더 구조에 있는, 다음의 INODE 페이지 리스트에 사용되는 리스트 노드가 포함되어 있습니다:
파일 segment INODE 항목의 구조는 다음과 같습니다:
각 INODE 항목에서 리스트가 아닌 필드의 용도는 다음과 같습니다:
테이블이 커지면 fragment 배열이 가득 찰 때까지 각 파일 segment에 개별 페이지를 할당한 다음, 한 번에 1개의 extent를 할당하고 결국 한 번에 4개의 extent를 할당하도록 전환합니다.
Extent descriptor의 list base node는 각 파일 segment INODE 항목에도 있습니다:
마지막으로 사용된 페이지가 NOT_FULL 리스트의 extent에서 해제되면, extent는 파일 segment의 FREE 리스트로 이동될 수 있지만 실제로는 space의 FREE 리스트로 이동됩니다.
INDEX 페이지에 대해서 아직 설명하진 않았지만, 한 가지 작은 사실에 대해 알 수 있습니다. 각 index의 FSEG 헤더의 root 페이지에는 index에 사용된 파일 segment를 나타내는 파일 segment INODE 항목에 대한 포인터가 있습니다. 각 index는 leaf 페이지와 leaf가 아닌 (내부) 페이지에 각각 하나의 파일 segment를 사용합니다. 이 정보는 FSEG 헤더 구조 (INDEX 페이지)에 저장됩니다:
위 그림에 존재하는 space ID는 다소 불필요합니다 --- 항상 현재 space ID와 동일하기 때문입니다. 페이지 번호와 offset은 INODE 페이지의 파일 segment INODE 항목을 가리킵니다. 완전히 비어있을지라도 두 파일 segment는 항상 존재합니다.
예를 들어, 새로 생성된 테이블에서, 존재하는 유일한 페이지는 root 페이지이며 (leaf 페이지이기도 하지만), “내부 (internal)” 파일 segment에 존재하므로 나중에 이동시킬 필요가 없습니다. “leaf” 파일 segment INODE 리스트와 fragment 배열은 모두 비어 있습니다. “내부” 파일 segment INODE 리스트는 모두 비어 있고, 단일 root 페이지는 fragment 배열에 있습니다.
다음 다이어그램은 index에 대한 전체 다중 레벨 구조를 보여줍니다:
Index root 페이지는 두 개의 inode (파일 segments)를 가리키며, 각각은 fragment 배열 (fragment 리스트에서 최대 32개의 개별 페이지를 가리킴)과 전체 extent 및 여러 리스트를 가지며, extent descriptor의 리스트 포인터를 사용하여 서로 연결됩니다. Extent descriptor는 extent를 참조하고 extent 내의 free 페이지를 추적하는 데 사용됩니다. Easy!
다음 포스트에서는 사용자 관점에서 가장 중요한 페이지 유형 중 하나인 INDEX 페이지의 구조를 살펴보겠습니다. 그 다음, 하이 레벨에서 InnoDB가 index를 어떻게 구성하는지 살펴보겠습니다.