Btrfs 파일시스템 심화

COW B-tree 아키텍처, 서브볼륨/스냅샷, 데이터 체크섬, 압축, RAID, 핵심 자료구조, 공간 관리, btrfs-progs, 성능 튜닝 종합 가이드.

개요 & 역사

Btrfs(B-tree File System, "버터 FS"로 발음)는 Copy-on-Write(COW) 기반의 차세대 Linux 파일시스템입니다. Oracle의 Chris Mason이 2007년 개발을 시작하여, Linux 2.6.29(2009)에서 메인라인에 병합되었습니다.

개발 역사

시기커널 버전주요 이정표
2007-Chris Mason(Oracle)이 개발 시작, ZFS 대안 목표
20092.6.29메인라인 병합, 기본 COW + B-tree 구조
20123.4send/receive, qgroup, device replace 추가
20133.9RAID 5/6 실험적 지원, skinny metadata
20184.14+zstd 압축 지원 추가
20205.5space_cache v2 (free space tree) 기본 활성화
20225.15+Fedora 33+ 기본 FS 채택, SUSE 엔터프라이즈 지원
20246.7+RAID1 성능 개선, extent tree v2 개발 진행

주요 스펙

항목
최대 볼륨 크기16 EiB
최대 파일 크기16 EiB (논리적), 실질적으로 볼륨 크기에 제한
최대 파일 수264
파일명 길이255 바이트
블록(섹터) 크기4K (기본), metadata nodesize 16K (기본)
체크섬crc32c (기본), xxhash, sha256, blake2b
내장 RAID0, 1, 10, 5/6 (실험적), DUP
COW전체 메타데이터 + 데이터 (기본)
압축zlib, lzo, zstd
타임스탬프 범위1970-01-01 ~ 2486 (나노초 정밀도)

설계 철학

Btrfs 핵심 원칙: 모든 메타데이터와 데이터를 B-tree로 관리하면서 COW를 통해 일관성을 보장합니다. 이로 인해 별도의 저널링 없이도 파일시스템 무결성을 유지하며, 스냅샷과 클론을 O(1)으로 생성할 수 있습니다.

Btrfs의 설계는 세 가지 핵심 원칙에 기반합니다:

아키텍처 & 디스크 레이아웃

Btrfs는 ext4처럼 고정 크기 Block Group을 사용하지 않습니다. 대신 Chunk 기반 동적 할당으로 스토리지를 관리하며, 모든 메타데이터를 B-tree에 저장합니다.

Btrfs 디스크 레이아웃 0 Superblock Sys Chunk Array Chunk (Metadata) Chunk (Data) Chunk (Data) Unallocated B-tree 계층 구조 Root Tree (tree of trees) FS Tree (subvol) Extent Tree Checksum Tree Chunk Tree Dev Tree Free Space Tree UUID Tree FS Tree 내부: INODE_ITEM DIR_ITEM / DIR_INDEX EXTENT_DATA

Superblock

Btrfs superblock은 디바이스의 고정 오프셋(64K, 64M, 256G)에 위치하며, 파일시스템 부팅에 필요한 핵심 정보를 담고 있습니다:

/* fs/btrfs/ctree.h */
struct btrfs_super_block {
    /* 체크섬: 첫 32바이트는 나머지 블록의 체크섬 */
    u8  csum[BTRFS_CSUM_SIZE];       /* 32B */
    u8  fsid[BTRFS_FSID_SIZE];       /* 16B - 파일시스템 UUID */
    __le64 bytenr;                    /* superblock 자신의 위치 */
    __le64 flags;
    __le64 magic;                     /* "_BHRfS_M" */
    __le64 generation;                /* 트랜잭션 세대 번호 */
    __le64 root;                      /* Root Tree의 루트 블록 위치 */
    __le64 chunk_root;                /* Chunk Tree 루트 */
    __le64 log_root;                  /* Tree-log 루트 */
    __le64 total_bytes;               /* 전체 크기 */
    __le64 bytes_used;                /* 사용된 바이트 */
    __le64 num_devices;               /* 디바이스 수 */
    __le32 sectorsize;                /* 섹터 크기 (보통 4096) */
    __le32 nodesize;                  /* B-tree 노드 크기 (보통 16384) */
    __le16 csum_type;                 /* 체크섬 알고리즘 */
    /* ... 추가 필드 생략 ... */
    u8  sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE]; /* 부트스트랩 chunk 매핑 */
} __attribute__((packed));

핵심 B-tree 종류

TreeObject ID역할
Root Tree1"tree of trees" — 모든 서브 트리의 루트 포인터 저장
Extent Tree2데이터/메타데이터 extent의 할당 상태, 역참조(back-reference)
Chunk Tree3논리 → 물리 주소 매핑 (Chunk 할당)
Dev Tree4물리 디바이스별 할당 상태
FS Tree5+ (서브볼륨별)파일/디렉토리 메타데이터, extent 참조
Checksum Tree7데이터 extent의 체크섬
Free Space Tree10블록 그룹별 미사용 공간 (space_cache v2)
UUID Tree9서브볼륨 UUID → ID 매핑 (send/receive용)
Quota Tree8서브볼륨 공간 할당량(qgroup) 추적
Tree-Log-7~-8fsync 최적화용 임시 트리 (재부팅 시 replay)

B-tree 노드 구조

Btrfs의 모든 B-tree 노드는 nodesize(기본 16K) 크기이며, 헤더 + 아이템 배열로 구성됩니다:

/* B-tree 노드 헤더 */
struct btrfs_header {
    u8     csum[BTRFS_CSUM_SIZE];  /* 노드 전체의 체크섬 */
    u8     fsid[BTRFS_FSID_SIZE];  /* 파일시스템 UUID */
    __le64 bytenr;                /* 이 노드의 논리 주소 */
    __le64 flags;
    u8     chunk_tree_uuid[BTRFS_UUID_SIZE];
    __le64 generation;             /* COW 시점의 트랜잭션 번호 */
    __le64 owner;                  /* 이 노드가 속한 tree ID */
    __le32 nritems;                /* 아이템 수 */
    u8     level;                  /* 0=리프, 1+=내부 노드 */
};

/* 리프 노드 아이템 */
struct btrfs_item {
    struct btrfs_disk_key key;   /* (objectid, type, offset) */
    __le32 offset;                /* 리프 내 데이터 시작 오프셋 */
    __le32 size;                  /* 데이터 크기 */
};

Chunk 매핑

Btrfs는 논리 주소(logical address)와 물리 주소(physical address)를 분리합니다. Chunk Tree가 논리 → 물리 매핑을 담당하며, 이를 통해 여러 디바이스에 걸친 RAID 배치가 가능합니다:

/* Chunk 매핑 정보 */
struct btrfs_chunk {
    __le64 length;           /* chunk 크기 */
    __le64 owner;            /* Extent Tree objectid */
    __le64 stripe_len;       /* stripe 길이 (보통 64K) */
    __le64 type;             /* DATA | METADATA | SYSTEM + RAID 프로파일 */
    __le16 num_stripes;      /* stripe 수 */
    __le16 sub_stripes;      /* RAID10용 sub-stripe 수 */
    struct btrfs_stripe stripe[];  /* 가변 길이 stripe 배열 */
};

Copy-on-Write (COW)

COW는 Btrfs의 가장 근본적인 메커니즘입니다. 기존 블록을 직접 수정하지 않고 새 위치에 수정된 데이터를 기록한 후 포인터를 갱신합니다.

COW (Copy-on-Write) 동작 흐름 Before (기존 상태) Root Node Node A Node B Leaf 1 Leaf 2 ★ After (COW 후) Root' (new) Node A' (new) Node B Leaf 1 Leaf 2' (new) COW Leaf 2 수정 → Leaf 2' 새로 할당 → Node A' 새로 할당 → Root' 새로 할당 Node B는 변경 없으므로 기존 블록 공유 (O(log N) 복사) ★ 수정된 리프 / 음영 = 새로 할당된 노드 / 기존 노드는 가비지 컬렉션 대상

COW 동작 원리

파일의 한 블록을 수정하면 다음 과정이 일어납니다:

  1. 새로운 데이터 블록에 수정된 내용 기록
  2. 해당 리프 노드를 새 위치에 COW (새 데이터 블록 포인터 포함)
  3. 부모 노드들을 루트까지 재귀적으로 COW
  4. Superblock에 새 루트 위치 기록 (atomic commit point)
  5. 이전 블록들은 해제 대상이 됨 (스냅샷이 참조하지 않는 경우)

트랜잭션 모델

Btrfs는 COW를 기반으로 세대(generation) 기반 트랜잭션을 구현합니다:

/* fs/btrfs/transaction.h */
struct btrfs_transaction {
    u64 transid;                /* 트랜잭션 ID (generation) */
    atomic_t num_writers;         /* 현재 writer 수 */
    atomic_t use_count;           /* 참조 카운트 */
    enum btrfs_trans_state state; /* RUNNING → COMMIT_START → COMMIT_DOING → COMMITTED */
    struct list_head list;       /* 트랜잭션 리스트 */
    struct extent_io_tree dirty_pages; /* 더티 페이지 추적 */
};

/* 트랜잭션 참여 */
struct btrfs_trans_handle *btrfs_start_transaction(
    struct btrfs_root *root, unsigned int num_items);
int btrfs_commit_transaction(struct btrfs_trans_handle *trans);

커밋 과정은 두 단계로 진행됩니다:

  1. 트랜잭션 실행: 모든 수정 사항이 메모리에 COW로 반영되고, 새 블록들이 디스크에 기록됨
  2. 슈퍼블록 기록: 모든 데이터가 디스크에 안착한 후 슈퍼블록의 root 포인터를 원자적으로 갱신 (commit point)

NODATACOW

주의: NODATACOW 설정 시 해당 파일의 데이터는 COW가 비활성화되어 체크섬도 비활성화됩니다. 데이터베이스 파일이나 VM 이미지처럼 랜덤 쓰기가 빈번한 경우에 유용하지만, 데이터 무결성 검증이 불가능해집니다.
# 특정 파일/디렉토리에 NODATACOW 속성 설정
$ chattr +C /path/to/file

# 마운트 옵션으로 전체 적용
$ mount -o nodatacow /dev/sda1 /mnt

# 상태 확인
$ lsattr /path/to/file
---------------C---- /path/to/file

서브볼륨 & 스냅샷

서브볼륨(subvolume)은 Btrfs의 핵심 기능으로, 하나의 파일시스템 내에서 독립적인 POSIX 파일 트리를 형성합니다. 각 서브볼륨은 고유한 FS Tree(tree ID)를 가지며, 스냅샷은 서브볼륨의 특수한 형태입니다.

서브볼륨과 스냅샷 구조 Top-level (ID=5) @rootfs (ID=256) @home (ID=257) @snap-home (ID=258) [RO] 공유 extent (COW) 스냅샷(@snap-home)은 @home과 동일한 extent를 COW로 공유 수정이 발생할 때만 새 블록이 할당됨 (공간 효율적)

서브볼륨 개념

# 서브볼륨 생성
$ btrfs subvolume create /mnt/@rootfs
$ btrfs subvolume create /mnt/@home

# 서브볼륨 목록 확인
$ btrfs subvolume list /mnt
ID 256 gen 100 top level 5 path @rootfs
ID 257 gen 100 top level 5 path @home

# 특정 서브볼륨으로 마운트
$ mount -o subvol=@rootfs /dev/sda1 /
$ mount -o subvol=@home   /dev/sda1 /home

# 서브볼륨별 마운트 (subvolid 사용)
$ mount -o subvolid=256 /dev/sda1 /

스냅샷

스냅샷은 COW를 활용하여 서브볼륨의 시점 복사본을 O(1) 시간에 생성합니다:

# 읽기 전용 스냅샷 (백업/복구에 권장)
$ btrfs subvolume snapshot -r /home /mnt/@snap-home-$(date +%Y%m%d)

# 쓰기 가능 스냅샷
$ btrfs subvolume snapshot /home /mnt/@home-work

# 스냅샷 삭제
$ btrfs subvolume delete /mnt/@snap-home-20240101

# 스냅샷에서 롤백 (서브볼륨 교체 방식)
$ btrfs subvolume delete /mnt/@rootfs
$ btrfs subvolume snapshot /mnt/@snap-rootfs-good /mnt/@rootfs

Send/Receive

Btrfs send/receive는 스냅샷 간 차이(delta)를 스트림으로 전송하여 증분 백업이나 원격 복제를 구현합니다:

# 전체 전송 (최초 백업)
$ btrfs send /mnt/@snap-day1 | btrfs receive /backup/

# 증분 전송 (이전 스냅샷 대비 변경분만)
$ btrfs send -p /mnt/@snap-day1 /mnt/@snap-day2 | btrfs receive /backup/

# SSH를 통한 원격 전송
$ btrfs send -p /mnt/@snap-day1 /mnt/@snap-day2 | \
    ssh remote_host btrfs receive /backup/

# 파일로 저장
$ btrfs send /mnt/@snap-day1 -f /tmp/snap-day1.btrfs
팁: send/receive는 읽기 전용 스냅샷에서만 동작합니다. 증분 전송의 경우 소스와 대상 모두에 기준 스냅샷(-p)이 존재해야 합니다. btrfs send--compressed-data 옵션(커널 5.18+)은 압축된 데이터를 그대로 전송하여 CPU 사용을 줄입니다.

기본 서브볼륨

# 기본 서브볼륨 변경 (부팅 시 자동 마운트될 서브볼륨)
$ btrfs subvolume set-default 256 /mnt

# 현재 기본 서브볼륨 확인
$ btrfs subvolume get-default /mnt
ID 256 gen 100 top level 5 path @rootfs

데이터 무결성

Btrfs는 모든 데이터와 메타데이터에 체크섬을 적용하여 bit rot, 불량 섹터, 기타 무음 데이터 손상(silent corruption)을 탐지합니다.

체크섬 알고리즘

알고리즘크기유형커널 버전특징
crc32c4B비암호화기본CPU 가속(SSE4.2), 높은 성능, 대부분의 워크로드에 적합
xxhash8B비암호화5.5+crc32c 대비 더 강한 충돌 저항, ARM에서 빠름
sha25632B암호화5.5+높은 보안 수준, 성능 오버헤드 큼
blake2b32B암호화5.5+sha256 대비 빠른 암호화 해시, 보안 + 성능 균형
# 파일시스템 생성 시 체크섬 알고리즘 지정 (포맷 후 변경 불가)
$ mkfs.btrfs --csum xxhash /dev/sda1
$ mkfs.btrfs --csum blake2b /dev/sda1

# 현재 사용 중인 체크섬 확인
$ btrfs inspect-internal dump-super /dev/sda1 | grep csum_type
csum_type               xxhash64 (2)

Checksum Tree

데이터 블록의 체크섬은 Checksum Tree(tree ID=7)에 저장됩니다. 각 항목은 연속된 데이터 블록 범위의 체크섬 배열입니다:

/* fs/btrfs/file-item.c - 체크섬 조회 */
int btrfs_lookup_csums_range(
    struct btrfs_root *root,
    u64 start,             /* 시작 바이트 오프셋 */
    u64 end,               /* 끝 바이트 오프셋 */
    struct list_head *list, /* 결과 체크섬 리스트 */
    int search_commit       /* 커밋된 루트에서 검색 여부 */
);

/* 체크섬 검증 흐름:
 * 1. 데이터 블록 읽기
 * 2. Checksum Tree에서 저장된 체크섬 조회
 * 3. 읽은 데이터로 체크섬 계산
 * 4. 비교 → 불일치 시 -EIO 또는 미러에서 복구 시도
 */

Scrub

Scrub은 온라인 상태에서 전체 파일시스템의 데이터 무결성을 검증하는 백그라운드 작업입니다:

# scrub 시작
$ btrfs scrub start /mnt

# 진행 상태 확인
$ btrfs scrub status /mnt
UUID:             12345678-...
Scrub started:    Mon Jan  1 00:00:00 2024
Status:           running
Duration:         0:05:32
Total to scrub:   100.00GiB
Bytes scrubbed:   45.20GiB (45.20%)
Rate:             140.00MiB/s
Error summary:    csum=0

# scrub 일시 중지 / 재개
$ btrfs scrub cancel /mnt
$ btrfs scrub resume /mnt

자가 복구

RAID1/10/DUP 구성에서 체크섬 불일치가 감지되면, Btrfs는 정상 미러에서 데이터를 읽어 손상된 복사본을 자동으로 복구합니다:

/* fs/btrfs/raid56.c, volumes.c - 자가 복구 흐름 */
/*
 * 1. 읽기 I/O 완료 → 체크섬 검증 실패
 * 2. bio에서 미러 번호 확인
 * 3. 다른 미러(mirror_num)에서 동일 블록 재읽기
 * 4. 체크섬 검증 성공 시:
 *    - 정상 데이터를 요청자에게 반환
 *    - 손상된 미러에 정상 데이터를 기록 (repair)
 * 5. 모든 미러 실패 시 → -EIO 반환
 */
참고: 단일 디바이스 + DUP 메타데이터 구성에서도 메타데이터의 자가 복구가 가능합니다. 데이터의 자가 복구는 RAID1/10 또는 DUP 데이터 프로파일이 필요합니다.

압축

Btrfs는 파일 데이터의 투명 압축(transparent compression)을 지원하여 디스크 공간을 절약하고, 특정 워크로드에서는 I/O 대역폭도 개선합니다.

압축 알고리즘

알고리즘커널 버전압축률속도레벨특징
zlib초기높음느림1~9 (기본 3)전통적, 최고 압축률이 필요할 때
lzo2.6.38낮음빠름없음CPU 오버헤드 최소, 임베디드/SBC에 적합
zstd4.14높음빠름1~15 (기본 3)최신, 최적의 압축률/속도 균형 (권장)

압축 설정

# 마운트 옵션으로 전체 압축 활성화
$ mount -o compress=zstd /dev/sda1 /mnt
$ mount -o compress=zstd:3 /dev/sda1 /mnt          # 레벨 지정
$ mount -o compress-force=zstd /dev/sda1 /mnt       # 강제 압축 (비압축성 데이터 포함)

# 파일/디렉토리별 압축 설정 (btrfs property)
$ btrfs property set /mnt/logs compression zstd
$ btrfs property get /mnt/logs compression
compression=zstd

# 기존 파일 재압축
$ btrfs filesystem defragment -r -czstd /mnt/data/

내부 구조

Btrfs 압축은 최대 128K 단위로 동작합니다. 파일의 각 extent는 독립적으로 압축되며, 압축 유형은 extent 메타데이터에 기록됩니다:

/* fs/btrfs/ctree.h - extent 데이터 */
struct btrfs_file_extent_item {
    __le64 generation;
    __le64 ram_bytes;       /* 압축 전 크기 (원본) */
    u8     compression;      /* 0=없음, 1=zlib, 2=lzo, 3=zstd */
    u8     encryption;       /* 미사용 (예약) */
    __le16 other_encoding;   /* 미사용 */
    u8     type;             /* INLINE / REG / PREALLOC */
    __le64 disk_bytenr;     /* 디스크상의 시작 위치 */
    __le64 disk_num_bytes;  /* 압축 후 디스크 크기 */
    __le64 offset;          /* extent 내 오프셋 */
    __le64 num_bytes;       /* 논리 크기 */
};

/* 압축 비율 확인 */
$ compsize /mnt
Processed 12345 files, 6789 regular extents (7000 refs)
Type       Perc     Disk Usage   Uncompressed
TOTAL       65%       6.5G          10G
zstd        62%       5.8G          9.3G
none       100%       700M          700M

RAID & 멀티 디바이스

Btrfs는 볼륨 매니저(LVM) 없이 자체적으로 여러 디바이스를 관리하고 RAID를 구성합니다. 데이터와 메타데이터에 서로 다른 RAID 프로파일을 적용할 수 있습니다.

RAID 프로파일

프로파일최소 디바이스중복도가용 용량읽기 성능상태
single1없음100%1x안정
DUP12 복사 (같은 디바이스)~50%1x안정 (메타데이터 기본)
RAID02없음N × 최소Nx안정
RAID122 복사 (다른 디바이스)~50%2x안정
RAID1C333 복사~33%3x안정 (5.5+)
RAID1C444 복사~25%4x안정 (5.5+)
RAID1042 복사 + 스트라이핑~50%Nx안정
RAID531 패리티(N-1) × 최소(N-1)x불안정
RAID642 패리티(N-2) × 최소(N-2)x불안정
경고: RAID5/6은 프로덕션에서 사용하지 마십시오. Btrfs RAID5/6은 write hole 문제와 불완전한 복구 코드로 인해 여전히 불안정합니다. 데이터 손실 위험이 있으며, Btrfs 위키에서도 프로덕션 사용을 권장하지 않습니다. 패리티 기반 중복이 필요하면 mdadm RAID5/6 위에 Btrfs single 프로파일을 사용하십시오.
Btrfs RAID1 데이터/메타데이터 분리 배치 Device 1 (sda - 500G) Meta (RAID1) Data (RAID1) Device 2 (sdb - 500G) Meta (RAID1) Data (RAID1) 메타데이터: RAID1 (2 복사) | 데이터: RAID1 (2 복사) 각 chunk는 독립적으로 프로파일이 적용됨 일반적 권장 구성: 메타데이터 = RAID1 / 데이터 = RAID1 or RAID0 mkfs.btrfs -d raid1 -m raid1 /dev/sda /dev/sdb

디바이스 관리

# 멀티 디바이스 파일시스템 생성
$ mkfs.btrfs -d raid1 -m raid1 /dev/sda /dev/sdb

# 디바이스 추가
$ btrfs device add /dev/sdc /mnt
$ btrfs balance start -dconvert=raid1 -mconvert=raid1 /mnt

# 디바이스 제거
$ btrfs device remove /dev/sdb /mnt

# 결함 디바이스 교체 (온라인)
$ btrfs replace start /dev/sdb /dev/sdd /mnt
$ btrfs replace status /mnt

# 디바이스 사용량 확인
$ btrfs device usage /mnt
/dev/sda, ID: 1
   Device size:           500.00GiB
   Data,RAID1:            200.00GiB
   Metadata,RAID1:         10.00GiB
   System,RAID1:           32.00MiB
   Unallocated:           289.97GiB

프로파일 변환

# single → RAID1 변환 (온라인)
$ btrfs balance start -dconvert=raid1 -mconvert=raid1 /mnt

# RAID1 → RAID10 변환 (4+ 디바이스 필요)
$ btrfs balance start -dconvert=raid10 /mnt

# 필터를 사용한 부분 변환
$ btrfs balance start -dconvert=raid1,soft /mnt  # 이미 raid1인 chunk는 건너뜀

핵심 커널 자료구조

Btrfs 커널 코드(fs/btrfs/)의 핵심 자료구조를 살펴봅니다.

btrfs_key

B-tree의 모든 아이템은 (objectid, type, offset) 3-tuple 키로 정렬됩니다:

struct btrfs_key {
    __le64 objectid;   /* 대상 객체 ID (inode 번호, tree ID 등) */
    u8     type;       /* 아이템 타입 */
    __le64 offset;     /* 타입별 의미 다름 (offset, size 등) */
};

주요 아이템 타입:

상수objectid 의미offset 의미
BTRFS_INODE_ITEM_KEY1inode 번호0
BTRFS_DIR_ITEM_KEY84부모 inode이름의 crc32c 해시
BTRFS_DIR_INDEX_KEY96부모 inode시퀀스 번호
BTRFS_EXTENT_DATA_KEY108inode 번호파일 내 오프셋
BTRFS_EXTENT_ITEM_KEY168바이트 오프셋extent 크기
BTRFS_CHUNK_ITEM_KEY228tree ID (보통 256)논리 오프셋
BTRFS_ROOT_ITEM_KEY132tree ID0 또는 transid

btrfs_fs_info

파일시스템 전체의 런타임 상태를 관리하는 최상위 구조체:

/* fs/btrfs/fs.h */
struct btrfs_fs_info {
    struct btrfs_root  *tree_root;       /* Root Tree */
    struct btrfs_root  *chunk_root;      /* Chunk Tree */
    struct btrfs_root  *extent_root;     /* Extent Tree */
    struct btrfs_root  *csum_root;       /* Checksum Tree */
    struct btrfs_root  *uuid_root;       /* UUID Tree */
    struct btrfs_root  *free_space_root; /* Free Space Tree */
    struct super_block *sb;              /* VFS super_block */

    u64 generation;                       /* 현재 트랜잭션 세대 */
    u64 last_trans_committed;              /* 마지막 커밋된 세대 */

    struct btrfs_transaction *running_transaction;
    struct btrfs_space_info *data_sinfo;   /* 데이터 공간 정보 */
    struct btrfs_space_info *meta_sinfo;   /* 메타데이터 공간 정보 */

    unsigned long mount_opt;               /* 마운트 옵션 비트필드 */
    u32 sectorsize;                       /* 섹터 크기 */
    u32 nodesize;                         /* B-tree 노드 크기 */
    /* ... 수백 개의 필드 생략 ... */
};

btrfs_root

/* fs/btrfs/ctree.h */
struct btrfs_root {
    struct rb_node        rb_node;        /* fs_info의 rbtree에 연결 */
    struct extent_buffer  *node;           /* 루트 노드 (메모리) */
    struct extent_buffer  *commit_root;    /* 커밋된 루트 (읽기용) */
    struct btrfs_root_item root_item;      /* 디스크 아이템 */
    struct btrfs_key       root_key;       /* Root Tree 내의 키 */
    struct btrfs_fs_info  *fs_info;        /* 역참조 */
    u64 root_key_objectid;                  /* tree ID */
    u64 last_trans;                          /* 마지막 수정 트랜잭션 */
};

btrfs_inode

/* fs/btrfs/btrfs_inode.h */
struct btrfs_inode {
    struct btrfs_root     *root;           /* 소속 FS Tree */
    struct btrfs_key       location;       /* (ino, INODE_ITEM, 0) */
    u64                    disk_i_size;    /* 디스크 상 파일 크기 */
    u64                    generation;     /* 생성 트랜잭션 */
    u64                    flags;          /* NODATACOW, COMPRESS 등 */
    struct extent_io_tree  io_tree;        /* I/O 상태 추적 */
    struct inode           vfs_inode;      /* VFS inode (임베딩) */
};

btrfs_path

B-tree 검색/순회에 사용되는 경로 구조체:

/* fs/btrfs/ctree.h */
struct btrfs_path {
    struct extent_buffer *nodes[BTRFS_MAX_LEVEL]; /* 루트→리프 노드 배열 */
    int                   slots[BTRFS_MAX_LEVEL]; /* 각 레벨의 슬롯 인덱스 */
    u8                    locks[BTRFS_MAX_LEVEL]; /* 잠금 상태 */
    int                   keep_locks;              /* 순회 시 잠금 유지 */
    int                   lowest_level;             /* 검색 중단 레벨 */
};

/* B-tree 검색 예시 */
struct btrfs_path *path = btrfs_alloc_path();
struct btrfs_key key = { .objectid = ino, .type = BTRFS_INODE_ITEM_KEY, .offset = 0 };
int ret = btrfs_search_slot(trans, root, &key, path, 0, 0);
/* ret == 0: 정확한 키 발견
 * ret > 0: 키 없음, path는 삽입 위치
 * ret < 0: 에러 */
btrfs_free_path(path);

공간 관리

Btrfs의 공간 관리는 블록 그룹(Chunk), 미사용 공간 추적, balance, defrag 등 여러 메커니즘으로 구성됩니다.

Btrfs 공간 할당 계층 Block Group Allocator Space Info (data / meta / system) Chunk Allocator Block Group (1~수 GiB 단위) Free Space Cache/Tree Used Extents Free Space Used Extents Free Space (unallocated)

블록 그룹

Btrfs는 스토리지를 블록 그룹(Block Group) 단위로 관리합니다. 각 블록 그룹은 하나의 Chunk에 대응하며, DATA/METADATA/SYSTEM 타입 중 하나를 가집니다:

# 블록 그룹 할당 상태 확인
$ btrfs filesystem usage /mnt
Overall:
    Device size:                   1.00TiB
    Device allocated:            500.03GiB
    Device unallocated:          523.97GiB
    Used:                        400.00GiB

Data,RAID1: Size:240.00GiB, Used:195.50GiB (81.46%)
Metadata,RAID1: Size:10.00GiB, Used:5.20GiB (52.00%)
System,RAID1: Size:32.00MiB, Used:48.00KiB (0.15%)

미사용 공간 추적

Btrfs는 두 가지 방식으로 블록 그룹 내 미사용 공간을 추적합니다:

방식저장 위치특징
space_cache v1숨겨진 inode 파일기존 방식, 마운트 시 전체 로드
space_cache v2 (Free Space Tree)전용 B-tree (tree ID=10)5.15+ 기본값, COW 보호, 빠른 마운트
# space_cache v2 강제 활성화
$ mount -o space_cache=v2 /dev/sda1 /mnt

# space_cache 상태 확인
$ btrfs inspect-internal dump-super /dev/sda1 | grep cache
compat_ro_flags         0x1
                        ( FREE_SPACE_TREE | FREE_SPACE_TREE_VALID )

Balance

Balance는 블록 그룹 간 데이터를 재배치하는 작업입니다. RAID 프로파일 변환, 디바이스 추가/제거 후 균등 분배, 공간 회수에 사용됩니다:

# 전체 balance (모든 블록 그룹 재배치 - 시간 많이 소요)
$ btrfs balance start /mnt

# 사용률이 낮은 블록 그룹만 balance (공간 회수)
$ btrfs balance start -dusage=50 /mnt    # 사용률 50% 미만 데이터 그룹
$ btrfs balance start -musage=50 /mnt    # 사용률 50% 미만 메타데이터 그룹

# balance 상태 / 취소
$ btrfs balance status /mnt
$ btrfs balance cancel /mnt

Defrag

# 파일 단편화 해소
$ btrfs filesystem defragment /mnt/large_file

# 디렉토리 재귀 defrag + 압축 적용
$ btrfs filesystem defragment -r -czstd /mnt/data/

# defrag 범위 지정 (오프셋 + 길이)
$ btrfs filesystem defragment -s 0 -l 1G /mnt/large_file
주의: defrag은 COW 특성상 스냅샷과 공유하던 extent의 공유를 해제합니다. 스냅샷이 많은 환경에서 defrag을 수행하면 디스크 사용량이 급증할 수 있습니다.

Resize

# 파일시스템 확장 (온라인)
$ btrfs filesystem resize +100G /mnt
$ btrfs filesystem resize max /mnt      # 디바이스 전체로 확장

# 파일시스템 축소 (온라인, 주의 필요)
$ btrfs filesystem resize -50G /mnt

# 특정 디바이스만 resize (devid 지정)
$ btrfs filesystem resize 1:+100G /mnt

ENOSPC 문제

Btrfs는 Chunk 단위 할당 방식 때문에 디스크에 빈 공간이 있어도 ENOSPC가 발생할 수 있습니다:

# ENOSPC 진단
$ btrfs filesystem usage /mnt           # allocated vs unallocated 확인
$ btrfs filesystem df /mnt              # 타입별 사용량

# ENOSPC 대처: 사용률 낮은 블록 그룹 재배치
$ btrfs balance start -dusage=0 /mnt    # 빈 데이터 그룹 해제
$ btrfs balance start -dusage=10 /mnt   # 10% 미만 그룹 통합
$ btrfs balance start -musage=10 /mnt   # 메타데이터도 동일

관리 도구 (btrfs-progs)

btrfs-progs는 Btrfs 사용자 공간 도구 모음입니다. btrfs 명령어를 통해 파일시스템 관리의 모든 측면을 제어합니다.

주요 명령어

명령어설명예시
mkfs.btrfs파일시스템 생성mkfs.btrfs -L myfs -d raid1 /dev/sd{a,b}
btrfs filesystemFS 관리 (usage, df, resize, defrag, show)btrfs fi usage /mnt
btrfs subvolume서브볼륨 관리 (create, delete, list, snapshot)btrfs sub list /mnt
btrfs device디바이스 관리 (add, remove, usage)btrfs dev usage /mnt
btrfs balance블록 그룹 재배치btrfs bal start -dusage=50 /mnt
btrfs scrub데이터 무결성 검증btrfs scrub start /mnt
btrfs replace디바이스 교체btrfs replace start /dev/sda /dev/sdb /mnt
btrfs send/receive스냅샷 전송/수신btrfs send /snap | btrfs receive /backup
btrfs check오프라인 검사 (fsck)btrfs check /dev/sda1
btrfs rescue복구 도구btrfs rescue super-recover /dev/sda1
btrfs property속성 관리btrfs prop set /dir compression zstd
btrfs quota할당량 관리btrfs quota enable /mnt
btrfs qgroup쿼터 그룹 관리btrfs qgroup show /mnt

filesystem usage

# 가장 유용한 공간 확인 명령어
$ btrfs filesystem usage /mnt
Overall:
    Device size:                   1.00TiB
    Device allocated:            600.03GiB
    Device unallocated:          423.97GiB
    Device missing:                  0.00B
    Device slack:                    0.00B
    Used:                        450.00GiB
    Free (estimated):            350.00GiB      (min: 250.00GiB)
    Free (statfs, df):           350.00GiB
    Data ratio:                       2.00
    Metadata ratio:                   2.00
    Global reserve:              512.00MiB      (used: 0.00B)
    Multiple profiles:                  no

Data,RAID1: Size:280.00GiB, Used:220.00GiB (78.57%)
   /dev/sda      280.00GiB
   /dev/sdb      280.00GiB

Metadata,RAID1: Size:20.00GiB, Used:5.00GiB (25.00%)
   /dev/sda       20.00GiB
   /dev/sdb       20.00GiB

System,RAID1: Size:32.00MiB, Used:48.00KiB (0.15%)
   /dev/sda       32.00MiB
   /dev/sdb       32.00MiB

Unallocated:
   /dev/sda      211.97GiB
   /dev/sdb      211.97GiB

inspect-internal

# superblock 정보 덤프
$ btrfs inspect-internal dump-super /dev/sda1

# B-tree 덤프 (디버깅용)
$ btrfs inspect-internal dump-tree /dev/sda1

# 특정 tree만 덤프
$ btrfs inspect-internal dump-tree -t 2 /dev/sda1  # Extent Tree

# inode → 경로 역참조
$ btrfs inspect-internal inode-resolve 256 /mnt
/mnt/some/file.txt

# 논리 주소 → 물리 주소 변환
$ btrfs inspect-internal logical-resolve 12345678 /mnt

성능 튜닝

Mount 옵션

옵션기본값설명
compress=zstd:N없음투명 압축 (zstd 권장, 레벨 1~15)
compress-force=zstd없음비압축성 파일도 강제 압축 시도
ssd자동 감지SSD 최적화 활성화
discard=async없음비동기 TRIM (SSD 필수)
noatimerelatimeatime 갱신 비활성화 (성능 향상)
space_cache=v2v2 (5.15+)Free Space Tree 사용
autodefrag없음자동 조각 모음 (랜덤 쓰기 워크로드)
commit=N30트랜잭션 커밋 주기(초)
thread_pool=N코어 수 + 2워커 스레드 수
max_inline=N2048인라인 extent 최대 크기
nodatacow없음COW 비활성화 (DB, VM 이미지용)
flushoncommit없음커밋 시 강제 flush (안정성 ↑, 성능 ↓)

SSD 최적화

# SSD에 최적화된 마운트 예시
$ mount -o compress=zstd:1,ssd,discard=async,noatime,space_cache=v2 \
    /dev/nvme0n1p2 /

# fstab 예시
UUID=xxx  /  btrfs  defaults,compress=zstd:1,ssd,discard=async,noatime,space_cache=v2,subvol=@  0 0
팁: discard=async는 커널 6.2+에서 크게 개선되었습니다. 동기 discard 대신 항상 discard=async를 사용하십시오. SSD에서 ssd 옵션은 대부분 자동 감지되므로 명시하지 않아도 됩니다.

워크로드별 튜닝

# 데스크톱 / 범용 서버
$ mount -o compress=zstd:1,noatime,discard=async,space_cache=v2 ...

# 데이터베이스 (MySQL, PostgreSQL)
$ mount -o nodatacow,noatime,discard=async ...
# + DB 데이터 디렉토리에 chattr +C 설정
$ chattr +C /var/lib/mysql/
$ chattr +C /var/lib/postgresql/

# 가상머신 이미지 저장소
$ mount -o nodatacow,noatime ...
$ chattr +C /var/lib/libvirt/images/

# 로그 서버 (대량 순차 쓰기)
$ mount -o compress=zstd:3,noatime,commit=120,autodefrag ...

# NAS / 미디어 스토리지
$ mount -o compress=zstd:3,noatime,space_cache=v2 ...

모니터링

# 실시간 할당 상태
$ watch -n 5 btrfs fi usage /mnt

# 디바이스 I/O 통계
$ btrfs device stats /mnt
[/dev/sda].write_io_errs    0
[/dev/sda].read_io_errs     0
[/dev/sda].flush_io_errs    0
[/dev/sda].corruption_errs  0
[/dev/sda].generation_errs  0

# 에러 카운터 리셋
$ btrfs device stats -z /mnt

# 커널 메시지에서 btrfs 관련 로그 확인
$ dmesg | grep -i btrfs

다른 파일시스템 비교

기능 비교표

기능Btrfsext4XFSZFS
COWOXX (reflink O)O
스냅샷O (서브볼륨 단위)X (LVM 의존)XO (데이터셋 단위)
데이터 체크섬OX (메타만)XO
투명 압축O (zlib/lzo/zstd)XXO (lz4/gzip/zstd)
내장 RAIDO (0/1/10/5/6)XXO (Z1/Z2/Z3)
인라인 dedupeX (오프라인만)XXO
Send/ReceiveOXXO
온라인 축소OOXX
최대 볼륨 크기16 EiB1 EiB8 EiB256 ZiB
라이선스GPLGPLGPLCDDL
안정성양호 (RAID5/6 제외)매우 높음매우 높음매우 높음

선택 가이드

사용 사례추천이유
엔터프라이즈 서버 (보수적)ext4 / XFS검증된 안정성, 성숙한 fsck 도구
데스크톱 / 워크스테이션Btrfs스냅샷 롤백, 압축, 유연한 관리
NAS / 스토리지 서버Btrfs / ZFS데이터 무결성, 스냅샷, RAID
컨테이너 호스트Btrfs서브볼륨, 스냅샷, overlay 성능
데이터베이스 전용ext4 / XFSCOW 오버헤드 없음, 예측 가능한 I/O
대용량 데이터 레이크XFS대규모 병렬 I/O 최적화
최고 수준 데이터 보호ZFS검증된 RAIDZ, 인라인 dedupe, 성숙도

Btrfs vs ZFS

두 파일시스템 모두 COW 기반이며 유사한 기능 세트를 제공하지만, 근본적인 차이가 있습니다:

측면BtrfsZFS
라이선스GPL v2 (커널 내장)CDDL (외부 모듈, OpenZFS)
RAID 안정성RAID1/10 안정, RAID5/6 불안정RAIDZ1/Z2/Z3 매우 안정
메모리 사용상대적으로 적음ARC 캐시로 대량 메모리 사용 (RAM 절반 이상)
커널 통합메인라인 포함별도 DKMS/kmod 설치 필요
인라인 Dedupe미지원 (오프라인만)지원
Send/Receive지원지원
축소온라인 축소 가능불가
성숙도발전 중매우 성숙 (Solaris 계보)
팁: 순수 Linux 환경에서 커널 통합과 간편한 관리를 원하면 Btrfs, 대규모 스토리지의 최고 수준 데이터 보호와 검증된 RAID를 원하면 ZFS를 선택하십시오.