Skip to main content

Command Palette

Search for a command to run...

Slotted Page

Published
4 min read
Slotted Page

데이터베이스와 관련된 기술을 보다보면 어떻게 데이터를 관리하고 저장하지? 특히 단편화(Fragmentation) 이 일어나는 것을 어떻게 통제하고 관리할까? 혹은 정렬된 자료구조 내부에서 데이터의 순서를 보존하기 위해 어떠한 행위들을 할까? 궁금해집니다. 오늘은 조금 더 데이터베이스 내부에 쓰이는 자료구조를 들여다보며 연관된 행위를 공부해보려고 합니다.

Fixed-size

데이터를 넣을때 저희가 넣는 데이터는 보통 사이즈가 안정해져있는 경우가 많습니다. 이를 variable-size records 라고 호칭하는데요. 이러한 데이터를 넣게 되면 가변 크기의 Page 를 읽거나 쓰는데 오버헤드가 생기거나 복잡해져 Fixed-size 의 Page 로 read, write 를 하는 방식이 대부분의 데이터베이스에서 이뤄집니다. (물론 variable-size 로 저장하는 방식도 있습니다)

Fixed-size 의 경우 좋아보이지만 아래와 같이 내부 단편화(Internal Fragementation) 문제가 발생합니다.

가변 길이 데이터를 저장하기 위해 페이지 내부를 N byte 단위의 **고정 크기 슬롯(또는 세그먼트)**으로 쪼개어 관리한다고 가정해 봅시다. 이때 M byte의 데이터를 저장한다면? N - (M modulo N) byte 만큼의 공간이 낭비됩니다.

실제로 64 byte 를 N 으로 우리가 저장하려는 레코드의 사이즈 M 을 70 으로 잡으면 58 byte 만큼의 공간이 낭비됩니다. 대부분 실생활의 어플리케이션에서 저장되는 데이터들은 사이즈가 가변인 경우가 많으므로 내부 단편화가 지속적으로 생기게 됩니다.

이러한 문제를 어떻게 해결할 수 있을까요? 가장 간단한 방법으로는 부족한 공간을 기억하고 있다가 하나의 Page 로 치환할 정도의 공간이 나온다면 레코드를 여유가 되는 위치에 삽입하는 방법입니다. 하지만 이렇게 되면 실제로 저장된 레코드의 오프셋이 이동이 되어 메타데이터를 저장하고 있는 부분에 베타적인 Lock 을 거는 행위등이 발생 할 수 있고 꽤나 큰 오버헤드가 발생할 수 있습니다.

Slotted Page

이러한 문제를 해결하기 위해 Slotted Page 라는 개념이 도입되게 됩니다. Slotted Page 는 Pointer 영역Cell 영역을 나누어 관리합니다. (Page Header 영역도 있습니다)

Pointer Array

포인터 배열 영역은 실제 데이터가 저장된 위치(Offset) 을 가르키는 포인터의 배열입니다. 페이지의 앞부분인 Header 바로 뒷 부분에 위치합니다.

Postgresql 을 공부해보셨다면 이 개념을 Heap 에서 보셨을 거라 생각이 듭니다.

장점

왜 Pointer 영역이 존재할까요? 위에도 언급했지만 실제 저장된 Record 는 사이즈가 크기 때문에 재 정렬을 위한 이동과정에서 많은 오버헤드가 발생합니다. 하지만 실제 저장된 데이터는 가만히 있고, 참조하는 Pointer 의 위치만 바꾸게 되면 실제 데이터는 움직이지 않았지만 정렬 된 것 처럼 보이게 되는 것이죠.

또한 외부에서 실제 Actual Record 를 참조하게 된다면 실제 Record 가 저장된 offset 을 기억해야 합니다. 즉, 이 offset 관리에 또 overhead 가 발생됩니다. 이는 단편화가 발생한 지역을 청소하는 시점에 또 다른 오버헤드로 부가됩니다.

지금 처럼 Pointer 로 관리되는 구조에서는 외부에서는 Pointer 를 통해 간접 참조만 시행하면 됩니다. 즉, 실제 값의 Actual offset 을 참조할 일이 없어지는 것이죠.

단점

단점으로는 아래와 같이 크게 두가지가 존재합니다.

  • actual offset 을 참조하지 않고 pointer 를 통해 참조하므로 간접 참조 비용 발생

  • pointer array 를 저장하기 위한 추가 저장공간 필요

위와 같은 단점이 있지만 단점을 상쇄할만큼의 이점이 있어 Postgresql 과 같은 데이터베이스에서는 Pointer Array 를 운용합니다.

Cell 영역

Cell 영역은 페이지의 맨 뒷 부분부터 시작되어 앞쪽을 향해 실제로 채워지는 데이터입니다. Pointer 영역과 역방향으로 성장하는 이유는 둘이 같은 방향으로 성장하게되면 빈 공간이 여러 공간으로 쪼개질 수 있는데, 역방향으로 성장하게 되면 빈 공간은 이 두 영역의 중간 공간에만 생기기 때문입니다.

실제 데이터는 가변 크기(variable size) 의 레코드(Postgresql 의 경우 Tuple) 형태로 저장됩니다. Pointer 배열의 특정 Slot 이 이 Cell 의 시작지점을 가리키게 됩니다.

위에서 설명한대로 데이터가 가변적이다 보니 내부에 단편화 현상이 발생하게 됩니다. 가변 데이터의 특성상 이 구멍의 사이즈에 맞는 데이터가 들어오지 않는다면, 이 구멍은 영원히 채워지지 않은 상태로 존재하게 됩니다.

빈 공간 회수(Defragmentation / Compaction)

그래서 윈도우 사용자라면 익숙한 빈공간 회수를 위한 조각 모음(Compaction) 이 이뤄집니다. Cell 영역에 빈 공간이 많아지면 시스템에서 유효한 Cell 들을 모아 맨 끝쪽으로 재배치를 진행합니다. 이 과정에서 offset 이 변경되지만 외부에서는 pointer 를 통해 간접 참조하므로 문제가 발생하지 않습니다.

그림으로 이해하기

위 그림을 보면 첫번째 레코드를 삽입하면 전단 부분에 Slot (Pointer) 이 생성되고 실제 Record 가 저장된 Offset 을 가리키고 있는 것을 확인할 수 있습니다. 중간 부분은 Free Space 이고, Record 는 맨 뒷 부분에 기록됩니다.

데이터를 추가할때 마다 중간 Free Space 가 줄어듭니다. Record 2 를 만약 위 그림 처럼 삭제한다면 어떨까요? Record 1 과 3 사이에 구멍(Hole) 이 생기며 단편화가 발생하게 됩니다. 낭비된 공간의 회수를 위해 빈공간 회수를 해봅시다.

빈 공간 회수를 하면 위에서 설명했던 것과 같이 유효한 Cell 들만 모아 끝쪽으로 재배치하며 유효하지 않은 부분에 대한 공간을 회수하게 됩니다.

마치며

확실히 Database Internals 를 읽으면서 그간 배웠던 Postgresql 에 대한 내용도 정리되는 것 같다. 그리고 Gemini 3.1 은 확실히 전작보다 시각 적인 부분에서 코딩을 잘한다. 위의 예시들은 전부 Gemini 에게 시각화를 시키며 학습하였다.

More from this blog

RDB 에서 큰 컬럼을 인덱스로 잡으면 안되는 이유

B-Tree 는 기본적으로 페이지 사이즈 와 저장할 수 있는 원소의 개수를 고정값으로 사용한다. 하지만 우리가 실제로 페이지에 저장하는 값은 가변적인 크기를 가지고 있기 때문에 필연적으로 물리적으로 저장해야할 개수가 다 차기도 전에 페이지가 넘치는 상황에 부딪히게 된다. 예를 들어 100KB 를 저장하는 페이지에 위와 같이 데이터를 저장한 상태이다. 여

Feb 26, 20262 min read49

MCP 를 통한 workflow 자동화

AI native 최근에 LinkedIn 이나 여러 소셜 플랫폼들의 글을 보면 AI native 회사 라는 워딩들이 많이 보입니다. IBM 의 정의에 따르면 AI native 를 아래와 같이 정의한다고 하는데요. “AI를 사고와 업무 방식에 끊임없이 내재화하는 상태” 그렇다면 팀원들이 계속해서 AI 를 사고와 업무 방식에 끊임 없이 내재화 하려면 어떻게 해야할까요? 개발자들은 이미 Claude code 나 Codex 등 여러 AI Tool...

Feb 14, 20263 min read100

파이썬 톺아보기 2화 - Ast 와 바이트코드

식(Expression) 과 문장(Statement) 프로그래밍을 공부하다보면 위 두 단어를 반드시 마주하게 된다. 가끔 헷갈려하는 경우가 많은데 오늘은 python 에서 기본 모듈인 ast 모듈을 공부하며 이를 알아보도록 하자. 식(Expression) 기본적으로 식(Expression) 이란 평가되면 값이 나오는 코드 조각을 뜻한다. 파이썬에서는 어떠한 부분들이 있을까? 노드 타입설명예시 BinOp이항 연산a + b, x * y...

Feb 6, 20267 min read30
D

dev_roach

41 posts