본문으로 건너뛰기

버그 제보

이상한 점을 알려 주시면 고칠게요. 지금 보고 있는 페이지와 브라우저 정보가 함께 담겨요.

페이지:

Backend

DB 내부 구조: 쿼리 실행과 인덱스 자료 구조

작성 2026. 10. 21.추가
목차14개 항목
출처와 참고

ByteByteGo의 System Design 101 가이드 「How is a SQL Statement Executed in the Database?」, 「8 Data Structures That Power Your Databases」, 「B-Tree vs. LSM-Tree」, 「B+ 트리는 어쩌다 인덱스로 간택되었을까?」를 바탕으로 재구성한 노트다. 스토리지 엔진, 규칙·비용 기반 옵티마이저, B-트리와 B+트리 비교, 공간 인덱스, PostgreSQL 인덱스 종류는 「fru1tworld/cs-basic 모의 면접 질문 목록」의 질문을 실마리로 MySQL·PostgreSQL 공식 문서로 확인해 보강했다. Oracle 인스턴스의 메모리·프로세스·파일 구성과 논리 저장 구조는 고려사이버대학교 「데이터베이스 설계구축」(2023-2학기) 강의 내용으로 보강했다.

SQL 문장이 실행되는 과정

DB마다 구조는 다르지만 큰 흐름은 비슷하다. 앞쪽은 무엇을 할지 정하는 단계, 뒤쪽은 실제로 데이터를 만지는 단계다. SQL이 관계대수로 바뀌는 논리적 단계와 절의 평가 순서는 관계대수와 DB 설계에 있고, 여기서는 엔진 내부의 구성 요소를 따라간다.

flowchart TB
  client["클라이언트"] -->|"SQL (TCP 위의 DB 프로토콜)"| t["[1] 전송 계층<br/>연결·인증·세션"]
  t --> parser["[2] 파서<br/>구문 분석 → 의미 분석(테이블·컬럼 존재, 권한) → 쿼리 트리"]
  parser --> opt["[3] 옵티마이저<br/>통계로 비용을 추정해 실행 계획 선택<br/>(어느 인덱스? 어떤 조인 순서·방식?)"]
  opt --> exec["[4] 실행기<br/>계획대로 연산자(스캔, 조인, 정렬, 집계)를 돌린다"]
  exec --> am["[5] 접근 방법<br/>테이블·인덱스에서 행을 꺼내는 로직 (풀 스캔, 인덱스 스캔)"]
  am -->|"읽기"| buf["[6] 버퍼 관리자<br/>메모리의 페이지 캐시(버퍼 풀)에 있으면 바로,<br/>없으면 디스크에서 읽어 올린다"]
  am -->|"쓰기"| tx["[7] 트랜잭션 관리자<br/>로그(WAL·redo)를 먼저 쓰고 버퍼의 페이지를 고친다"]
  tx --> lock["[8] 잠금 관리자<br/>동시에 같은 데이터를 고치지 못하게 잠금을 건다"]
단계알아 둘 점
파서같은 SQL을 반복하면 파싱·계획 결과를 재사용할 수 있다. Prepared statement가 이 비용을 줄인다
옵티마이저비용 기반 옵티마이저는 통계에 의존한다. 통계가 낡으면 엉뚱한 계획을 고른다. EXPLAIN으로 고른 계획을 확인한다
버퍼 관리자대부분의 읽기는 디스크가 아니라 메모리 버퍼에서 끝난다. 버퍼 적중률이 성능을 좌우한다
트랜잭션 관리자WAL(Write-Ahead Logging): 데이터 페이지보다 로그를 먼저 디스크에 남겨, 장애 뒤에도 커밋된 변경을 되살린다
잠금 관리자격리 수준과 ACID의 격리성을 지킨다. 잠금 종류와 MVCC는 트랜잭션과 회복에 있다

스토리지 엔진

[5]~[8]처럼 데이터를 실제로 디스크에 어떻게 놓고, 읽고, 쓰고, 잠그고, 장애에서 되살릴지를 맡는 층을 스토리지 엔진(storage engine)이라 한다. 그 위의 SQL 층(파서·옵티마이저·실행기)은 「무엇을 원하나」를 해석하고, 엔진은 「행 하나 주세요」, 「이 행을 바꿔 주세요」 같은 요청을 처리한다.

엔진이 정하는 것예
저장 형식행을 클러스터드 B+트리에 둘까(InnoDB), 힙 파일에 쌓을까(PostgreSQL), LSM 트리로 쓸까(RocksDB)
인덱스 구조어떤 인덱스를 지원하나
트랜잭션·잠금트랜잭션 지원 여부, 잠금 단위(행·테이블), MVCC
회복redo·undo 로그, 크래시 복구
캐시버퍼 풀을 직접 관리하나, OS 페이지 캐시에 맡기나

MySQL은 이 층을 플러그인처럼 갈아 끼우는 구조라 테이블마다 엔진을 고른다(CREATE TABLE ... ENGINE=InnoDB). 기본값인 InnoDB는 트랜잭션·행 잠금·크래시 복구를 갖췄고, 옛 MyISAM은 트랜잭션이 없고 테이블 잠금만 한다. 그 밖에 MEMORY, 그리고 RocksDB를 쓰는 MyRocks가 있다. PostgreSQL은 엔진이 하나로 정해져 있다(12부터 테이블 접근 방식 API로 다른 저장 방식을 붙일 수는 있다). MongoDB의 WiredTiger처럼 NoSQL도 같은 개념의 층을 가진다.

옵티마이저: 규칙 기반과 비용 기반

구분규칙 기반(RBO, Rule-Based Optimizer)비용 기반(CBO, Cost-Based Optimizer)
계획을 고르는 법접근 경로마다 정해진 우선순위를 따른다. 「기본 키로 한 행 > 인덱스 범위 > 풀 스캔」처럼후보 계획마다 I/O·CPU 비용을 추정해 가장 싼 것을 고른다
쓰는 정보쿼리 모양과 인덱스 존재 여부통계: 행 수, 페이지 수, 컬럼의 고유값 수, 값 분포(히스토그램)
장점결과가 예측 가능하고 늘 같다데이터에 맞는 계획을 고른다. 행이 많이 걸리면 인덱스 대신 풀 스캔을 고르는 판단이 가능하다
단점데이터 분포를 몰라 인덱스가 오히려 느린 경우에도 인덱스를 쓴다통계가 낡거나 추정이 틀리면 엉뚱한 계획을 고른다. 데이터가 바뀌면 계획도 바뀔 수 있다

지금 주요 DBMS(MySQL, PostgreSQL, Oracle, SQL Server)는 모두 비용 기반이다. Oracle의 RBO는 10g부터 지원이 끝났다. 그래서 통계를 최신으로 유지하는 것(ANALYZE TABLE, PostgreSQL의 autovacuum이 함께 돌리는 ANALYZE)이 성능 관리의 일부다. 실행 계획을 읽는 법과 풀 스캔을 고르는 이유는 인덱스 설계와 쿼리 튜닝에 있다.

엔진의 몸체: Oracle로 보는 메모리·프로세스·파일

위 흐름의 버퍼 관리자·트랜잭션 관리자가 실제로 어떤 부품인지는 엔진 하나를 뜯어 보면 잘 보인다. Oracle은 부품마다 이름이 붙어 있어 예로 쓰기 좋다. 부품이 노리는 목표는 셋이다. 디스크를 덜 읽고, 커밋을 빨리 끝내고, 죽어도 되살린다. MySQL·PostgreSQL도 같은 목표를 비슷한 부품으로 푼다.

Oracle에서 인스턴스와 데이터베이스는 다른 말이다. 인스턴스는 메모리와 백그라운드 프로세스의 묶음이라 시작할 때 생기고 종료하면 사라진다. 데이터베이스는 디스크의 파일 묶음이라 인스턴스가 꺼져도 남는다. 인스턴스가 데이터베이스를 마운트해서 연다. 보통 1:1이고, RAC(Real Application Clusters)에서는 여러 서버의 인스턴스가 한 데이터베이스를 함께 연다.

flowchart TB
  cl["클라이언트"] --> sp["서버 프로세스<br>(전용 메모리 PGA)"] --> sga
  subgraph sga["SGA: 모든 세션이 공유"]
    direction LR
    bc["버퍼 캐시"] ~~~ shp["공유 풀"] ~~~ rlb["리두 로그 버퍼"] ~~~ lp["대용량·자바 풀"]
  end
  sga -->|"DBWn"| df["데이터 파일"]
  sga -->|"LGWR"| orl["온라인 리두 로그"] -->|"ARCn"| al["아카이브 로그"]
  sga -.- bg["CKPT·SMON·PMON …"]
  cf["컨트롤 파일<br>파일 위치와 체크포인트 기록"]

메모리: SGA와 PGA

영역담는 것MySQL·PostgreSQL의 대응
버퍼 캐시(SGA)데이터 파일에서 올린 블록. LRU 계열로 자주 쓰는 블록을 남긴다InnoDB 버퍼 풀, shared_buffers
공유 풀(SGA)파싱된 SQL·PL/SQL과 실행 계획(라이브러리 캐시), 테이블·권한 메타데이터(데이터 사전 캐시), 결과 캐시MySQL에는 실행 계획을 세션끼리 공유하는 캐시가 없다
리두 로그 버퍼(SGA)어느 블록이 어떻게 바뀌었나. 곧 리두 로그 파일로 내려간다InnoDB 로그 버퍼, WAL 버퍼
대용량·자바 풀(SGA)백업·병렬 처리용 큰 메모리, JVM 메모리-
PGA서버 프로세스마다 따로 갖는 정렬·해시 조인 작업 공간과 세션 정보. 모자라면 임시 테이블스페이스(디스크)로 넘친다work_mem, InnoDB의 정렬 버퍼

공유 풀은 문장 모양이 같은 SQL의 실행 계획을 재사용한다(소프트 파싱). 값을 문자열로 이어 붙이면 문장이 매번 달라 캐시를 못 쓰므로, Oracle에서는 바인드 변수가 인젝션 방어에 더해 성능에도 크게 듣는다(Prepared Statement).

백그라운드 프로세스

프로세스하는 일
서버 프로세스사용자 대신 SQL을 실행한다. 버퍼 캐시에 없는 블록은 직접 읽어 올린다. 연결당 하나가 기본(전용 서버)이다
DBWn버퍼 캐시의 고친(dirty) 블록을 데이터 파일에 쓴다. 커밋과 무관하게 나중에 몰아서 쓴다
LGWR리두 로그 버퍼를 리두 로그 파일에 쓴다. 커밋은 이 쓰기가 끝나야 완료다
CKPT체크포인트(여기까지는 데이터 파일에 다 내려갔다는 표시)를 기록하고 DBWn을 깨운다
SMON비정상 종료 뒤 재시작할 때 인스턴스 복구, 임시 세그먼트 정리
PMON죽은 세션의 트랜잭션을 롤백하고 잠금·자원을 푼다
ARCn다 쓴 리두 로그를 아카이브 로그로 복사한다(아카이브 모드)
RECO끊긴 분산 트랜잭션을 커밋이나 롤백으로 마무리한다
MMON 등성능 통계를 모아 AWR에 쌓는다

연결마다 프로세스를 두는 것은 PostgreSQL도 같고, MySQL은 연결마다 스레드를 둔다. 어느 쪽이든 연결이 비싸 앞단에 커넥션 풀을 둔다.

커밋은 리두 로그만 순차로 덧붙이고 끝낸다. 흩어진 데이터 블록을 제자리에 쓰는 무거운 일은 DBWn이 미룬다. 그 사이에 죽으면 재시작 때 리두로 커밋된 변경을 다시 적용하고(롤 포워드), 언두 세그먼트의 이전 값으로 커밋 안 된 변경을 되돌린다. 체크포인트가 잦으면 복구할 리두가 짧아지는 대신 쓰기가 는다. WAL과 재시작 복구의 일반 원리는 트랜잭션과 회복에 있다.

물리 파일

파일역할
데이터 파일테이블·인덱스의 실제 데이터
온라인 리두 로그변경 기록. 여러 그룹을 돌려 쓰고, 차면 다음 그룹으로 넘어간다(로그 스위치)
아카이브 리두 로그덮어쓰기 전에 떠 둔 리두 사본. 시점 복구(PITR)에 쓴다
컨트롤 파일DB 이름, 데이터 파일·리두 로그 위치, 체크포인트 정보. 잃으면 DB를 못 열어 여러 벌 둔다
파라미터 파일(spfile)메모리 크기 같은 인스턴스 설정

논리 저장 구조

SQL이 보는 테이블과 디스크 파일 사이에는 네 겹의 논리 단위가 있다.

flowchart TD
  ts["테이블스페이스"] ---|"1:N"| df["데이터 파일(물리)"]
  ts --> seg["세그먼트<br>테이블·인덱스·파티션 하나가 차지하는 공간"]
  seg --> ext["익스텐트<br>연속된 블록 묶음. 공간이 모자라면 하나씩 더 받는다"]
  ext --> blk["블록<br>I/O의 최소 단위. 기본 8KB(2~32KB 중 DB를 만들 때 정한다)"]
  • 블록이 크면 한 번에 많이 읽어 I/O 횟수가 줄지만, 몇 행만 필요할 때 낭비가 생기고 같은 블록을 두고 다투는 일이 는다. 분석용은 크게, OLTP는 기본값 근처를 쓴다. InnoDB의 페이지(기본 16KB)가 같은 개념이다.
  • 익스텐트는 블록을 연속으로 묶어 줘서 데이터가 흩어지지 않고 순차 읽기가 빨라진다.
  • 세그먼트는 한 테이블스페이스 안에 있지만 그 안의 여러 데이터 파일에 걸칠 수 있다.
  • 테이블스페이스는 관리 단위다. 테이블과 인덱스를 다른 디스크의 테이블스페이스에 두어 I/O를 나누거나, 오래된 데이터를 읽기 전용 테이블스페이스로 옮겨 백업 대상을 줄인다. 기본으로 SYSTEM·SYSAUX(데이터 사전), UNDO(롤백과 읽기 일관성용 이전 값), TEMP(메모리에 못 담은 정렬 중간 결과), USERS(사용자 객체 기본 위치)가 있다.
CREATE TABLESPACE ts_orders
  DATAFILE '/u01/oradata/orders01.dbf' SIZE 100M AUTOEXTEND ON NEXT 10M;
 
ALTER TABLESPACE ts_orders
  ADD DATAFILE '/u02/oradata/orders02.dbf' SIZE 100M;   -- 다른 디스크로 넓히기
 
-- 안의 객체, 걸린 참조 제약, 실제 파일까지 지운다. 되돌릴 수 없다
DROP TABLESPACE ts_orders INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS;

Oracle의 스키마는 사용자 계정과 1:1이다. HR 사용자가 만든 객체는 HR 스키마에 속하고, 다른 사용자는 HR.EMPLOYEES처럼 접두어를 붙이거나 동의어를 만들어 쓴다(동의어). 테이블 저장 방식은 순서 없이 쌓는 힙 테이블이 기본이고, 기본 키 순서의 B-트리에 행을 넣는 인덱스 구조 테이블(IOT)을 고를 수도 있다(클러스터드 인덱스).

에디션은 무료이고 CPU·메모리·용량 상한이 있는 Express Edition(XE), 중소 규모용 Standard Edition 2, 모든 기능을 갖춘 Enterprise Edition으로 나뉜다. 파티셔닝은 Enterprise Edition의 별도 유료 옵션이다. 12c부터는 컨테이너 DB(CDB) 하나에 플러그형 DB(PDB) 여럿을 담는 멀티테넌트 구조를 쓴다.

인덱스 자료 구조

어떤 자료 구조가 맞는지는 데이터가 메모리에 있나 디스크에 있나, 값의 형태(숫자·문자열·좌표·문서)가 무엇인가, 쓰기가 많나 읽기가 많나에 달렸다.

자료 구조위치원리강점쓰는 곳
B-트리 (B+트리)디스크정렬된 키를 고정 크기 페이지에 담은 균형 트리. 루트에서 키 범위를 따라 내려간다읽기·쓰기 성능이 고르다. 범위 조회거의 모든 관계형 DB의 기본 인덱스
해시 인덱스메모리·디스크키의 해시로 위치를 바로 찾는다같음(=) 조회가 O(1)키-값 저장소, PostgreSQL hash 인덱스
스킵 리스트메모리여러 층의 연결 리스트로 일부 노드를 건너뛰며 찾는다. 정렬을 유지한다구현이 단순하고 동시성 처리에 유리하다Redis 정렬 집합(sorted set), LSM 트리의 멤테이블
SSTable디스크키로 정렬된 불변 파일순차 쓰기, 병합이 쉽다LSM 트리의 디스크 계층
LSM 트리메모리 + 디스크멤테이블(스킵 리스트 등)에 쓰고, 차면 SSTable로 내린 뒤 백그라운드에서 병합한다쓰기 처리량이 높다Cassandra, RocksDB, LevelDB, ScyllaDB
역색인(inverted index)디스크단어 → 그 단어가 든 문서 목록전문 검색Lucene(Elasticsearch, OpenSearch), PostgreSQL GIN
접미사 트리메모리문자열의 모든 접미사를 트리로 만든다부분 문자열·패턴 검색문자열 검색, 생물 정보학
R-트리디스크공간 객체를 감싸는 사각형(MBR)을 계층으로 묶는다다차원 범위 조회, 가장 가까운 이웃공간 DB(PostGIS의 GiST 인덱스)

왜 B+트리인가

같은 균형 트리라도 레드-블랙 트리·AVL 트리는 노드 하나에 키 하나, 자식 둘이다. 디스크에서는 노드 하나를 읽을 때마다 페이지 하나만큼 I/O가 생기므로, 트리 높이가 곧 디스크 접근 횟수다.

항목이진 균형 트리B+트리
노드당 키1개페이지(InnoDB 기본 16KB)를 채울 만큼 수백 개
높이log₂Nlog_M N (M은 차수, 수백)
범위·순차 조회중위 순회가 필요하다정렬된 리프가 연결 리스트로 이어져 옆으로 훑으면 된다
데이터 위치모든 노드리프에만. 내부 노드는 길잡이 키만 담아 더 많은 키가 들어간다

M차 B+트리의 노드는 최소 ⌈M/2⌉개의 자식을 가지므로, 높이 h에서 담을 수 있는 키가 (M/2)^h 이상으로 늘어난다. 차수가 수백이면 수억 건도 높이 3~4 안에 들어가고, 루트와 상위 노드는 버퍼 풀에 상주하므로 실제 디스크 읽기는 한두 번이다. 모든 리프가 같은 깊이에 있어 어떤 키든 같은 비용으로 찾는다. 키가 길면 한 페이지에 들어가는 키 수가 줄어 트리가 높아지므로 인덱스 키는 짧을수록 좋다. 인덱스를 어떻게 설계할지는 인덱스 설계와 쿼리 튜닝에 있다.

B-트리와 B+트리는 같은 계열이지만 데이터를 두는 곳이 다르다.

항목B-트리B+트리
값(또는 행 포인터) 위치모든 노드리프에만. 내부 노드는 길잡이 키만 둔다
키 중복키가 트리에 한 번만 나온다내부 노드의 키가 리프에 다시 나온다
점 조회찾는 키가 위쪽 노드에 있으면 일찍 끝난다늘 리프까지 내려간다. 대신 비용이 일정하다
범위·순차 조회트리를 위아래로 오가며 순회해야 한다리프의 연결 리스트를 옆으로 훑는다
팬아웃내부 노드에 값도 담아 작다내부 노드에 키만 담아 크다. 트리가 더 낮다

B+트리가 늘 낫지는 않다. 무작위 점 조회만 있는 작업에서는 위쪽에서 끝날 수 있는 B-트리가 유리할 수 있고, 내부 노드에 키를 한 번 더 저장하는 공간이 든다. 같음 조회만 필요하면 해시 인덱스가 더 빠르다. 그래도 DB는 범위 조회·정렬·순차 스캔이 흔하고 높이가 낮을수록 디스크 I/O가 줄어서 B+트리를 기본으로 쓴다. 현업에서 「B-트리 인덱스」라고 부르는 것도 실제로는 대부분 B+트리다.

공간 인덱스: R-트리와 지오해시

B+트리는 값을 한 줄로 정렬해야 쓸 수 있다. 위도·경도는 두 값이 함께 가까워야 「가까운 곳」이므로, 한쪽으로 정렬하면 다른 쪽 조건은 거르지 못한다. 그래서 공간 데이터는 따로 처리한다.

방법원리쓰는 곳
R-트리가까운 객체들을 감싸는 최소 사각형(MBR)을 노드로 삼고, 그 사각형들을 다시 큰 사각형으로 묶는 균형 트리. 질의 영역과 겹치지 않는 사각형의 하위 트리는 통째로 건너뛴다MySQL InnoDB SPATIAL INDEX, PostGIS(GiST 위의 R-트리), 반경·영역 검색, 최근접 이웃
지오해시(Geohash)위도·경도 범위를 번갈아 반으로 나눈 비트를 섞어(interleave) base32 문자열로 만든다. 접두사가 같으면 같은 칸 안이다2차원을 1차원 문자열로 바꾸므로 일반 B+트리·키-값 저장소로 공간 검색을 할 수 있다. Redis GEO는 52비트 지오해시를 정렬 집합의 점수로 쓴다
쿼드트리, S2, H3공간을 4분할하거나 구면 셀·육각형 격자로 나눈 셀 ID를 키로 쓴다지도 서비스, 차량 배차

지오해시는 칸 경계 문제가 있다. 바로 옆이라도 경계 건너편이면 접두사가 전혀 다를 수 있어서, 반경 검색은 내 칸과 주변 8칸을 함께 조회한 뒤 실제 거리로 다시 거른다. 칸 길이(문자열 자릿수)는 검색 반경에 맞춰 고른다.

PostgreSQL의 인덱스 종류

PostgreSQL은 접근 방식(access method)을 골라 인덱스를 만든다(CREATE INDEX ... USING gin (...)).

종류구조유리한 경우
B-tree (기본)균형 트리=, <, >, BETWEEN, IN, 정렬, 접두사 LIKE 'abc%'(C 콜레이션이나 text_pattern_ops)
Hash해시 버킷=만. 키가 길 때 B-tree보다 작을 수 있다. 10부터 WAL에 기록돼 복제·복구에 안전하다
GiST범용 균형 트리 틀. R-트리 같은 구조를 그 위에 구현한다기하·지리(PostGIS), 범위 타입의 겹침(&&), 최근접 이웃 정렬(<->), 배제 제약(예약 시간 겹침 금지)
SP-GiST균형이 아닌 공간 분할 트리(쿼드트리, k-d 트리, 기수 트리)고르게 퍼지지 않은 데이터, 점 데이터, IP 주소, 전화번호 같은 접두사
GIN역색인. 값 하나가 여러 키(원소)를 가질 때 「키 → 행 목록」배열 포함(@>), jsonb 검색, 전문 검색(tsvector), pg_trgm과 함께 쓰면 LIKE '%abc%'. 갱신이 느려 대기 목록(fastupdate)에 모았다가 반영한다
BRIN블록 범위마다 최솟값·최댓값 같은 요약만 저장아주 크고 물리 순서와 값 순서가 맞는 테이블(추가만 하는 로그의 시각 컬럼). 인덱스가 B-트리보다 훨씬 작지만 정확도는 블록 범위 단위다

확장으로 더 붙는다. pgvector는 벡터 유사도 검색용 HNSW·IVFFlat 인덱스를 더한다.

이 밖에 열 단위 저장에 쓰는 비트맵 인덱스, 「없음」을 빠르게 판정하는 블룸 필터, 벡터 유사도 검색용 HNSW 같은 그래프 인덱스도 있다.

B-트리와 LSM 트리

디스크 기반 저장 엔진의 두 갈래다.

B-트리: 제자리 갱신

flowchart LR
  root["루트 페이지"] --> inner["내부 페이지"] --> leaf["리프 페이지: 키·값<br/>해당 페이지를 찾아 고쳐 쓴다"]

LSM 트리: 추가 후 병합

flowchart TB
  w["쓰기"] --> wal["WAL(장애 대비)"]
  w --> mem["멤테이블(메모리, 정렬)"]
  mem -->|"가득 차면 flush"| l0["L0: SSTable · SSTable · SSTable"]
  l0 -->|"컴팩션(compaction): 겹치는 파일을 병합·정렬, 지운 값 제거"| l1["L1: SSTable ── SSTable"]
  l1 --> l2["L2 ..."]
  r["읽기<br/>멤테이블 → L0 → L1 … 순으로 찾는다<br/>(블룸 필터로 없는 파일은 건너뜀)"]
항목B-트리LSM 트리
쓰기페이지를 찾아 제자리에서 고친다. 임의 I/O메모리에 쓰고 디스크에는 순차로 쓴다. 쓰기가 빠르다
읽기트리 높이만큼 페이지를 읽으면 끝. 읽기가 빠르고 예측 가능하다여러 계층을 뒤질 수 있다. 블룸 필터와 캐시로 보완한다
공간페이지 분할로 빈 공간이 생긴다병합 전에는 같은 키의 옛 버전이 여러 파일에 남는다
숨은 비용페이지 분할, 단편화컴팩션이 CPU·디스크 대역폭을 쓰고, 그동안 지연이 튈 수 있다
맞는 곳읽기 위주, 범위 조회, 트랜잭션쓰기 위주(로그, 이벤트, 메시지, 시계열)

세 가지 증폭으로 비교하면 정리가 쉽다. 읽기 증폭(한 번 읽으려고 몇 번 디스크를 보나), 쓰기 증폭(한 번 쓴 데이터가 디스크에 몇 번 다시 쓰이나), 공간 증폭(실제 데이터 대비 디스크를 얼마나 쓰나). B-트리는 읽기 증폭이 작고, LSM 트리는 쓰기 처리량을 위해 읽기·공간 증폭을 받아들인다. 와이드 컬럼 DB에서 LSM 트리가 쓰이는 방식은 와이드 컬럼 DB와 대규모 메시지 저장에 있다.

정리

  • SQL은 파싱 → 최적화 → 실행 → 버퍼·트랜잭션·잠금 관리 순으로 처리된다. 느린 쿼리는 대개 옵티마이저가 고른 계획에서 원인이 보인다.
  • 쓰기는 WAL로 먼저 기록하고, 읽기는 버퍼 풀에서 끝나는 것이 보통이다.
  • Oracle로 보면 인스턴스(공유 메모리 SGA + 프로세스 전용 PGA + 백그라운드 프로세스)가 파일 묶음인 데이터베이스를 연다. 커밋은 LGWR의 리두 쓰기로 끝나고 데이터 블록은 DBWn이 나중에 내린다. 저장은 테이블스페이스 → 세그먼트 → 익스텐트 → 블록이다.
  • 인덱스 자료 구조는 값의 형태와 읽기·쓰기 비율로 고른다. 범위 조회는 B-트리, 전문 검색은 역색인, 공간은 R-트리다.
  • B-트리는 읽기에, LSM 트리는 쓰기에 강하다.

관련 노트: 트랜잭션과 회복, 데이터베이스 확장과 성능 최적화, 와이드 컬럼 DB와 대규모 메시지 저장: Cassandra·ScyllaDB, 검색엔진의 원리: 역인덱스와 랭킹, 트리와 이진 탐색 트리, DB 커넥션 풀

원본 기록7

댓글

GitHub 계정으로 댓글을 남길 수 있어요.