본문으로 건너뛰기
김신건의 로그

[DB Internals] R-Tree: 공간 인덱스, MBR, split

· 수정 · 📖 약 1분 · 448자/단어 #r-tree #spatial #index #gis #postgis
R-Tree, R-tree spatial index, MBR, Minimum Bounding Rectangle, PostGIS spatial index, GiST R-Tree, R* Tree

정의

R-Tree (Guttman, 1984) = 공간 데이터 (2D, 3D) 를 인덱싱하는 balanced tree. 각 노드는 Minimum Bounding Rectangle (MBR) 을 저장.

IMPORTANT

지도, GIS, 위치 기반 서비스, 3D 게임, CAD 의 표준. PostGIS, MySQL Spatial, SQL Server Spatial 모두 R-Tree 계열.

왜 B-Tree 로 안 되나?

공간 데이터: (x, y) 좌표
Query: "이 사각형 안의 점들"

B-Tree 는 1차원 정렬만 가능:

-- ✗ 각 축을 별도 인덱스 → 매우 비효율
CREATE INDEX ON points(x);
CREATE INDEX ON points(y);
SELECT * FROM points WHERE x BETWEEN 10 AND 20 AND y BETWEEN 30 AND 40;
-- 두 인덱스 AND, 대부분 안 씀

R-Tree = n차원 공간의 계층적 분할.

구조 (2D 예)

flowchart TB
    Root["Root MBR: (0,0)-(100,100)"]
    Root --> A["A MBR: (0,0)-(50,60)"]
    Root --> B["B MBR: (40,50)-(100,100)"]
    A --> A1["Leaf: 점들 in (0,0)-(30,40)"]
    A --> A2["Leaf: 점들 in (20,30)-(50,60)"]
    B --> B1["Leaf: 점들 in (40,50)-(70,80)"]
    B --> B2["Leaf: 점들 in (60,70)-(100,100)"]

각 노드가 자기 자식들을 감싸는 최소 사각형 (MBR) 저장. B-Tree 처럼 balanced.

MBR (Minimum Bounding Rectangle)

점: (10, 20)
선분: (5, 5) - (15, 25) → MBR = (5,5)-(15,25)
원: 중심 (10, 10), 반지름 5 → MBR = (5,5)-(15,15)
폴리곤: 모든 꼭짓점 감싸는 최소 사각형

R-Tree 는 실제 도형이 아닌 MBR 로 계산 → 빠름. Precision 은 별도 filter 로.

sequenceDiagram
    autonumber
    Q->>Root: query rectangle (Q)
    Root->>Root: 자식 MBR 중 Q 와 *overlap* 하는 것만
    Root->>A: overlap? YES → 재귀
    Root->>B: overlap? NO → skip
    A->>A1: overlap? YES
    A->>A2: overlap? NO
    A1-->>Q: candidate 점 반환
    Q->>Q: 실제 도형 vs Q 확인 (refinement)

2 단계 필터링:

  1. Filter: MBR 겹침 확인 (빠름, 근사)
  2. Refinement: 실제 도형 vs 쿼리 (정확, 느림)

Insertion + Split

새 도형 → 가장 적게 확장되는 자식 선택 → leaf 도달:

insert(root, entry):
  if leaf:
    add entry to leaf
    if leaf overflow:
      split(leaf)
  else:
    best_child = choose_subtree(node, entry)
    insert(best_child, entry)
    update MBR of node

Split 알고리즘 (핵심)

flowchart LR
    Overflow["Leaf overflow (M+1 entries)"]
    Overflow --> Strategy{"Split 전략"}
    Strategy --> Linear["Linear split (Guttman)"]
    Strategy --> Quad["Quadratic split (더 좋은 quality)"]
    Strategy --> RStar["R* Tree (best quality)"]
알고리즘시간품질
LinearO(N)낮음
QuadraticO(N²)중간
R* TreeO(N log N)최고

R* Tree (2026 표준)

Beckmann et al. (1990) 의 개선:

  • Overflow 시 재삽입 (forced reinsertion).
  • Overlap 최소화 + coverage 최소화 + dead space 최소화.
  • PostGIS 의 GiST 가 R* 기반.

R-Tree vs Quad-tree vs KD-Tree

R-TreeQuad-treeKD-Tree
Balanced아니오아니오
Disk I/O최적나쁨나쁨
Dimension낮음 (2-6)2D낮음 (2-10)
DB 사용표준드묾드묾
삽입복잡간단복잡
갱신느림빠름매우 느림 (rebuild)

PostGIS 사용

CREATE EXTENSION postgis;

CREATE TABLE places (
  id BIGSERIAL PRIMARY KEY,
  name TEXT,
  location GEOMETRY(Point, 4326)   -- WGS 84
);

CREATE INDEX idx_places_geom ON places USING gist(location);

-- 반경 검색
SELECT * FROM places
WHERE ST_DWithin(location, ST_MakePoint(126.97, 37.55)::geography, 1000);

-- 사각형 검색
SELECT * FROM places
WHERE location && ST_MakeEnvelope(126.9, 37.5, 127.0, 37.6, 4326);

PostGIS 는 GiST + R-Tree. && (overlap), <@ (contains), ~= (equal).

PostGIS 의 성능 팁

-- 1. Geometry 대신 geography (구면 거리 정확)
location GEOGRAPHY(Point, 4326)

-- 2. SP-GiST 는 point 만 (더 빠름)
CREATE INDEX ON places USING spgist(location);

-- 3. Clustered index 로 physical order 개선
CLUSTER places USING idx_places_geom;

-- 4. Materialized bounding box column (자주 검색시)
ALTER TABLE places ADD COLUMN bbox BOX2D
  GENERATED ALWAYS AS (ST_Envelope(location)) STORED;
CREATE INDEX ON places USING gist(bbox);

GiST vs SP-GiST vs BRIN (공간)

인덱스적합
GiST일반 (Point, Polygon, Range) - R-Tree 기반
SP-GiSTPoint only, Quadtree - 더 빠름
BRIN시계열 좌표 (센서 데이터)

자세한 건 gin-index-deep / gin-gist-hash-indexes.

R-Tree 알고리즘 (3D+)

flowchart TB
    Q["R-Tree 확장"]
    Q --> RD["R-Tree in R^d (d dimensions)"]
    Q --> Priority["Priority R-Tree (I/O 최적)"]
    Q --> M["M-Tree (metric space, arbitrary distance)"]
    Q --> HR["Hilbert R-Tree (space-filling curve)"]

M-Tree = 임의 거리 함수 (문자열 편집 거리 등) 가능. HNSW (벡터 검색) 은 완전히 다른 접근.

자세한 벡터 검색은 elasticsearch-vector-search, Redis Vector Search.

흔한 함정

WARNING

  1. && (overlap) 만 쓰고 refinement 안 함 = MBR 겹치는 false positive. ST_Intersects 로 정밀.
  2. Geography vs Geometry 혼용 = 거리 단위 다름. 명시.
  3. SRID 불일치 = 검색 실패. ST_Transform 으로 통일.
  4. 큰 폴리곤의 R-Tree = MBR 이 너무 커서 false positive 폭증. 폴리곤 분할.

관련 위키

이 글의 용어 (7개)
[DB Internals] B-Tree 내부: split, merge, search 알고리즘database-internals
정의 B-Tree (Bayer & McCreight, 1972) = 디스크 I/O 최적화된 balanced multi-way search tree. 모든 leaf 깊이 동일 + …
[DB Internals] B+ Tree 내부: leaf linked list, range scandatabase-internals
정의 B+ Tree = B-Tree 의 변형. 모든 데이터가 leaf 에만, internal node 는 navigation key만. Leaf 들이 linked list 로 연…
[DB Internals] GIN Index 깊이: 구조, jsonb_path_ops, tsvectordatabase-internals
정의 GIN (Generalized Inverted Index) = 한 값이 여러 항목 을 가지는 데이터 (배열, JSON, tsvector) 의 역색인. PostgreSQL 의…
[DB] GIN / GiST / Hash / BRIN: 비-B-tree 인덱스database-internals
정의 B-tree 외 PostgreSQL 의 특수 인덱스 들. 자세한 B-tree 는 참고. | 인덱스 | 적합 | |---|---| | B-tree | = < > BETWEEN…
[DB] PostgreSQL: 프로세스 모델, MVCC, WAL, 확장성database-internals
정의 PostgreSQL 은 오픈소스 ORDBMS. 1986 UC Berkeley POSTGRES 의 후예. MVCC, 확장 가능 타입, JSONB, full-text searc…
[Redis] Vector Search: HNSW, SVS-VAMANA, Semantic Cachedatabase-internals
정의 Vector Search 는 임베딩 벡터의 거리/유사도 기준으로 근접한 K 개 를 찾는 검색. Redis 는 RediSearch 모듈 (Redis 8 부터는 코어) 과 Va…
[Search] ES Vector Search: kNN, ELSER, RAGsearch
정의 ES 의 벡터 검색 = 임베딩 기반 의미 검색. kNN (Approximate Nearest Neighbor) 으로 수억 벡터 안에서 가까운 K개 찾기. 2026 시점의 E…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기