파일과 디렉터리는 모두 운영체제 내부의 파일 시스템이 관리하는 객체이다.
파일은 하드 디스크나 SSD와 같은 보조기억장치에 저장된 관련 정보의 집합을 의미한다. 단순히 데이터 덩어리가 아니라, 데이터와 함께 이를 설명하는 부가 정보를 포함한다.
이 부가 정보를 파일 속성, 혹은 메타데이터라 한다.
| 파일 메타데이터 항목 | 설명 |
|---|---|
| 유형 | 일반 파일, 디렉터리, 장치 파일 등 |
| 크기 | 파일이 차지하는 바이트 수 |
| 보호 | 접근 권한 정보 |
| 소유자 | 사용자 및 그룹 정보 |
| 시간 정보 | 생성, 수정, 접근 시간 |
파일을 생성, 읽기, 쓰기, 삭제하는 모든 작업은 사용자 프로그램이 직접 수행하지 않는다. 운영체제가 제공하는 시스템 호출(system call)을 통해서만 가능하다. 이는 파일 접근의 일관성과 보호를 보장하기 위함이다.
파일을 효율적으로 관리하기 위해 디렉터리 개념을 사용한다.
현대 운영체제는 여러 계층을 가진 트리 구조 디렉터리를 사용한다.
/이 구조로 인해 자연스럽게 경로(path) 개념이 등장한다.
| 구분 | 설명 |
|---|---|
| 절대 경로 | 루트 디렉터리부터 시작하는 전체 경로 |
| 상대 경로 | 현재 작업 디렉터리를 기준으로 한 경로 |
대부분의 운영체제는 디렉터리를 특별한 형태의 파일로 취급한다.
디렉터리 파일에는 “어떤 파일이 존재하는지”, “해당 파일이 보조기억장치의 어디에 위치하는지”에 대한 정보가 저장된다.
또한 다음과 같은 특수 디렉터리를 사용한다.
| 표현 | 의미 |
|---|---|
. | 현재 작업 디렉터리 |
.. | 부모 디렉터리 |
파일 시스템은 파일과 디렉터리를 보조기억장치에 저장하고 접근할 수 있도록 하는 운영체제 내부 프로그램이다.
하나의 컴퓨터에서 여러 파일 시스템을 동시에 사용할 수 있다.
대표적인 파일 시스템으로 FAT 파일 시스템과 유닉스 파일 시스템이 존재한다.
보조기억장치를 사용하기 위해서는 파티션과 포매팅 과정이 필요하다.
파티셔닝은 저장장치를 여러 개의 논리적인 영역으로 구획하는 작업이다.
| 장점 | 설명 |
|---|---|
| 논리적 분리 | 운영체제, 데이터 영역 분리 가능 |
| 안정성 | 한 파티션의 손상이 전체에 영향 미침 방지 |
| 관리 용이성 | 서로 다른 파일 시스템 적용 가능 |
AWS S3 버킷과의 개념적 유사성이 자주 언급되지만, 파티션은 물리 저장장치 기반의 논리 분할, 버킷은 객체 저장소의 네임스페이스 단위라는 점에서 다르다.
포매팅은 저장 장치를 완전히 삭제하는 작업으로 오해되기 쉽다. 실제 의미는 파일 시스템을 설정하고 데이터를 기록할 구조를 만드는 작업이다.
| 구분 | 설명 |
|---|---|
| 저수준 포매팅 | 물리적 섹터 생성 |
| 논리적 포매팅 | 파일 시스템 구조 생성 |
운영체제는 파일과 디렉터리를 블록 단위로 읽고 쓴다.
하나의 파일은 하나 이상의 블록에 걸쳐 저장된다.
Windows에서는 블록 대신 클러스터라는 용어를 사용한다.
| 할당 방식 | 특징 | 문제점 |
|---|---|---|
| 연결 할당 | 블록을 포인터로 연결 | 순차 접근만 가능, 중간 손상 시 이후 접근 불가 |
| 색인 할당 | 색인 블록 사용 | 색인 블록 오버헤드 |
색인 할당은 파일의 모든 블록 주소를 색인 블록에 저장한다. 이를 통해 임의 접근이 가능해지고, 연결 할당의 문제를 해결한다.
FAT(File Allocation Table)는 연결 할당을 테이블 형태로 구현한 파일 시스템이다.
| 특징 | 설명 |
|---|---|
| 중앙 테이블 | 파일의 다음 블록 정보 저장 |
| 구조 단순 | 구현이 쉬움 |
| 단점 | 대용량 파일에 비효율적 |
FAT 테이블이 손상되면 파일 전체 접근이 어려워지는 구조적 한계가 존재한다.
유닉스 파일 시스템은 i-node 구조를 기반으로 한다.
| 구성 요소 | 역할 |
|---|---|
| i-node | 파일 메타데이터 및 블록 주소 |
| 데이터 블록 | 실제 파일 내용 |
디렉터리는 파일 이름과 i-node 번호를 매핑하는 테이블 형태로 저장된다.
이를 통해 파일 이름과 실제 데이터 위치를 분리한다.
저널링 파일 시스템은 변경 사항을 로그에 먼저 기록한 뒤 실제 반영을 수행한다.
| 장점 | 설명 |
|---|---|
| 복구 속도 | 시스템 장애 후 빠른 복구 |
| 무결성 | 메타데이터 일관성 유지 |
NTFS, ext4 등이 저널링을 지원한다.
아래 내용은 객체 스토리지 자체에 대한 개념 설명을 중심으로 다시 정리한 서술이다.
표는 2개만 사용했다.
AWS, MINIO는 저장 구조 방식이 다른 것으로 안다. 어떤 차이지?
AWS S3, MINIO는 객체 스토리지이다.
객체 스토리지는 파일 시스템의 연장선이 아니다.
파일과 디렉터리를 전제로 설계된 저장 방식이 아니라, 데이터를 객체라는 단위로 다루는 저장 모델이다.
객체는 세 가지 요소로 구성된다.
데이터 본문, 메타데이터, 그리고 이를 식별하는 키이다.
이 세 요소는 분리되지 않고 하나의 논리 단위로 취급된다.
중요한 점은 객체 스토리지에는 위치 개념이 존재하지 않는다는 점이다.
전통적인 파일 시스템은 “어디에 저장되어 있는가”가 중요하다.
객체 스토리지는 “무엇이라는 이름으로 저장되어 있는가”만 중요하다.
이 차이로 인해 객체 스토리지는 디스크 주소, inode, 디렉터리 트리와 같은 구조를 내부적으로 유지하지 않는다.
대신 키 기반 조회를 통해 객체를 직접 찾는다.
AWS S3는 디렉터리를 저장하지 않는다.
사용자가 보는 디렉터리 구조는 키 문자열의 접두어(prefix)를 사람이 이해하기 쉽게 표현한 결과이다.
다음 경로는 디렉터리가 아니다.
dataset/train/image1.jpg
S3 내부에서는 이 전체 문자열이 하나의 키이다.
dataset이나 train이라는 디렉터리는 실체가 없다.
이 방식이 가능한 이유는 S3가 계층 구조 탐색이 아닌 완전한 키 기반 접근을 사용하기 때문이다.
객체 단위 저장의 가장 큰 장점은 전역적인 네임스페이스를 만들 수 있다는 점이다.
모든 객체는 동일한 규칙으로 식별되고 접근된다.
이로 인해 다음과 같은 특성이 자연스럽게 따라온다.
첫째, 저장 위치 추상화.
데이터가 어느 디스크, 어느 서버에 있는지는 사용자가 알 필요가 없다.
S3는 내부적으로 데이터를 여러 노드에 분산 저장하고, 필요 시 재배치한다.
둘째, 메타데이터 확장성.
파일 시스템의 메타데이터는 제한적이다.
객체 스토리지는 사용자 정의 메타데이터를 자유롭게 추가할 수 있다.
셋째, 대규모 분산 환경에 최적화.
객체는 불변 데이터에 가깝게 다뤄진다.
수정 대신 새로운 객체를 생성하는 방식이 일반적이다.
이 특성은 락 관리, 동시성 제어, 장애 복구를 단순하게 만든다.
사용자가 경험한
“하나의 디렉터리에서 만 개 조회보다, 여러 디렉터리에 나뉜 만 개 조회가 더 오래 걸린다”
라는 현상은 객체 스토리지의 내부 동작과 직접적으로 연결된다.
S3는 디렉터리를 탐색하지 않는다.
리스트 조회는 항상 키 목록을 조건에 맞게 스캔하는 작업이다.
즉, 다음 두 작업은 내부적으로 다르다.
후자의 경우, 요청 자체가 여러 번 발생한다.
S3에서 객체 목록을 조회할 때, 내부적으로는 키 정렬된 인덱스를 기반으로 범위 스캔이 일어난다.
prefix 조건은 이 범위 스캔의 시작점과 끝점을 결정하는 역할을 한다.
| 조회 방식 | 내부 동작 |
|---|---|
| 단일 prefix 조회 | 연속 범위 스캔 |
| 다중 prefix 조회 | prefix별 개별 스캔 |
여러 디렉터리에 나뉜 조회는 다음 문제를 동반한다.
이로 인해 총 객체 수가 같더라도, 체감 성능 차이가 발생한다.
아래 내용은 AWS S3를 중심으로 객체 스토리지에서의 키 설계 원칙과 디렉터리 구조 설계가 가지는 실제 함의를 서술형으로 정리한 것이다.
표는 2개만 사용했다.
객체 스토리지에서 디렉터리 구조는 실체가 아니다.
그럼에도 불구하고 키 설계가 중요한 이유는 조회, 분산, 비용, 성능에 직접적인 영향을 주기 때문이다.
S3에서 모든 객체는 키 문자열 하나로 식별된다.
디렉터리처럼 보이는 구조는 사람이 이해하기 쉬운 표현일 뿐이다.
그러나 이 키 문자열은 내부적으로 정렬, 파티셔닝, 스캔의 기준으로 사용된다.
즉, 디렉터리는 존재하지 않지만
디렉터리처럼 보이게 만든 키 설계는 시스템 동작에 실질적인 영향을 미친다.
S3는 키를 사전순으로 정렬된 인덱스 형태로 관리한다.
객체 목록 조회는 이 정렬된 키 공간에서 연속 범위를 스캔하는 작업이다.
prefix 조건은 스캔 범위를 제한하는 역할을 한다.
prefix가 짧을수록 스캔 범위가 넓어지고,
prefix가 길수록 스캔 범위가 좁아진다.
이로 인해 키의 앞부분에 무엇을 두는지가 성능에 직접적인 영향을 미친다.
많은 경우 사람이 이해하기 쉬운 구조를 우선 설계한다.
userA/2024/01/01/data.json
userB/2024/01/01/data.json
userC/2024/01/01/data.json
이 구조는 관리 측면에서는 직관적이다.
그러나 특정 날짜의 데이터를 모두 조회하려면,
각 user prefix에 대해 개별 요청이 필요하다.
이때 발생하는 문제는 다음과 같다.
객체 스토리지에서 키 설계의 출발점은
“데이터를 어떻게 저장할 것인가”가 아니라
“데이터를 어떻게 조회할 것인가”이다.
조회가 자주 발생하는 기준을 키의 앞쪽으로 이동시키는 것이 핵심이다.
2024/01/01/userA/data.json
2024/01/01/userB/data.json
2024/01/01/userC/data.json
이 구조에서는 특정 날짜 기준 조회가
하나의 prefix 스캔으로 가능하다.
S3는 내부적으로 키의 prefix를 기준으로
데이터를 분산 저장한다.
키 앞부분이 지나치게 단조로우면
특정 파티션에 요청이 집중된다.
과거에는 다음과 같은 패턴이 대표적인 병목 원인이었다.
logs/2024/01/01/...
모든 쓰기 요청이 동일한 prefix로 몰리면서
hot partition 문제가 발생했다.
현재는 S3가 이를 상당 부분 완화했지만,
여전히 키 분산은 중요하다.
| 기준 | 의미 |
|---|---|
| 조회 기준 | 가장 자주 조회되는 조건 |
| 정렬 기준 | 시간, ID, 타입 |
| 분산 기준 | prefix 다양성 |
| 불변성 | overwrite 회피 |
키는 단순한 경로가 아니라
데이터 접근 전략을 코드로 표현한 결과이다.
객체 스토리지에서 디렉터리 구조를 설계한다는 것은
파일 시스템 구조를 설계하는 것이 아니다.
다음과 같은 의미를 가진다.
디렉터리는 탐색 대상이 아니라
스캔 범위를 제어하는 도구이다.
객체 스토리지는 LIST, GET 요청마다 비용이 발생한다.
비효율적인 키 설계는 조회 횟수를 늘린다.
| 구조 | 조회 방식 |
|---|---|
| 단일 prefix | 1회 LIST |
| 다중 prefix | N회 LIST |
객체 수가 같더라도
조회 비용과 지연 시간은 구조에 따라 달라진다.
객체 스토리지에서의 디렉터리 구조는
사람을 위한 표현이 아니라
시스템을 위한 설계이다.
키 설계는
조회 패턴, 분산 특성, 비용 모델을
모두 반영한 결과여야 한다.
디렉터리가 없는 저장소에서
디렉터리를 설계한다는 것은
어디를 빠르게 스캔할 것인가를 결정하는 행위이다.
파일 시스템은 프로그램 실행을 전제로 설계되었다.
빠른 수정, 잦은 열기와 닫기, 랜덤 접근을 전제로 한다.
객체 스토리지는 데이터 보관을 전제로 설계되었다.
대용량, 낮은 변경 빈도, 높은 내구성을 전제로 한다.
이 차이로 인해 객체 스토리지는
“디렉터리가 없다는 제약”이 아니라
“디렉터리가 필요 없는 문제를 풀기 위한 구조”로 이해하는 것이 정확하다.
MILVUS의 저장 방식 또한 상당히 다른 것으로 알고 있음. 차이점은?
Milvus의 저장 방식은 전통적인 데이터베이스의 파일 저장 구조와 다르다.
Milvus는 파일 시스템 위에 데이터를 직접 쌓지 않는다.
대신 메타데이터와 실제 데이터를 명확히 분리하고, 실제 데이터는 객체 스토리지에 저장한다.
이 구조의 핵심은 다음 한 문장으로 요약된다.
Milvus는 “파일”을 저장하지 않고, 불변 데이터 조각을 객체로 저장한다.
Milvus에 데이터가 들어오면, 즉시 객체 스토리지에 기록되지 않는다.
먼저 메모리 기반 구조로 수집된다.
이 시점부터 해당 데이터는 수정되지 않는다.
삭제나 업데이트는 새로운 세그먼트를 생성하는 방식으로 처리된다.
Milvus에서 저장의 최소 논리 단위는 파일이 아니라 세그먼트이다.
세그먼트는 여러 개의 벡터와 그에 대응하는 필드를 묶은 데이터 블록이다.
하나의 세그먼트는 다음을 포함한다.
이 구성 요소들은 하나의 디렉터리처럼 보이지만, 실제로는 여러 객체로 분해되어 MinIO에 저장된다.
Milvus는 MinIO를 통해 객체를 저장한다.
각 세그먼트는 다수의 객체로 나뉘어 기록된다.
이 객체들은 다음과 같은 특성을 가진다.
이 방식은 객체 스토리지의 특성과 정확히 맞물린다.
Milvus는 객체 스토리지에 의미를 저장하지 않는다.
객체는 단순히 데이터 덩어리이다.
객체가 무엇을 의미하는지는 etcd가 알고 있다.
| 저장소 | 담당 정보 |
|---|---|
| etcd | 컬렉션, 파티션, 세그먼트 상태 |
| MinIO | 벡터, 인덱스, 로그 데이터 |
etcd는 데이터 위치를 직접 관리하지 않는다.
오직 “어떤 세그먼트가 존재하는가”, “현재 어떤 상태인가”만 기록한다.
Milvus가 POSIX 파일 시스템을 직접 사용하지 않는 이유는 구조적인 병목 때문이다.
파일 시스템 기반 저장은 다음 문제를 동반한다.
Milvus의 워크로드는 대량의 벡터를 한 번 쓰고 여러 번 읽는 패턴이다.
이 패턴은 객체 스토리지와 잘 맞는다.
검색 요청이 들어오면 Milvus는 다음 순서로 동작한다.
이 과정에서 로컬 파일 시스템 탐색은 발생하지 않는다.
모든 접근은 메타데이터 조회 → 객체 다운로드의 형태이다.
세그먼트가 불변이라는 점은 매우 중요하다.
삭제 연산 역시 기존 데이터를 수정하지 않는다.
삭제 마스크를 별도 객체로 관리한다.
Milvus는 저장 계층을 완전히 분리했기 때문에 다음이 가능하다.
객체 스토리지는 자체적으로 복제와 내구성을 제공한다.
Milvus는 이를 신뢰하고 상위 계층의 로직에 집중한다.
| 항목 | 파일 시스템 기반 DB | Milvus |
|---|---|---|
| 저장 단위 | 파일 | 세그먼트 객체 |
| 수정 방식 | in-place | append + replace |
| 메타데이터 | 파일 시스템 | etcd |
| 확장 | 제한적 | 수평 확장 |
Milvus의 저장 방식은
파일 시스템을 더 잘 쓰는 구조가 아니다.
파일 시스템을 전제하지 않는 구조이다.
객체 스토리지의 불변성, 키 기반 접근, 분산 친화적 특성을 그대로 받아들이고,
그 위에 세그먼트 중심의 데이터 모델을 얹었다.
이로 인해 Milvus는 대규모 벡터 데이터 환경에서도
저장과 검색을 분리한 안정적인 구조를 유지한다.
하나 또 별도로 궁금한게 MILVUS에는 파티션 기능이 존재해. 운영체제의 파일 시스템에서의 파티션과 비슷하다고 볼 수 있는거지? 그리고 MILVUS에서 파티션 조회와 필터 조회는 어떤 차이일까?
결론부터 말하면 비슷해 보이지만 같은 개념이 아니다.
Milvus의 파티션은 운영체제 파일 시스템의 파티션과 역할, 책임, 영향 범위가 완전히 다르다.
운영체제의 파일 시스템 파티션은 저장 장치 수준의 논리 분할이다.
디스크를 나누고, 각 영역에 파일 시스템을 설정한다.
이는 저장 위치와 물리 자원 관리에 직접적인 영향을 준다.
반면 Milvus의 파티션은 저장 구조가 아니라 논리적 데이터 그룹화이다.
저장 위치, 디스크 배치, 객체 스토리지 구조와는 직접적인 연관이 없다.
이를 명확히 구분하면 다음과 같다.
| 구분 | OS 파일 시스템 파티션 | Milvus 파티션 |
|---|---|---|
| 레벨 | 저장 장치 | 논리 데이터 |
| 물리 분리 | 있음 | 없음 |
| 저장 위치 영향 | 직접적 | 없음 |
| 목적 | 자원 관리 | 검색 범위 제한 |
Milvus 파티션은 저장 최적화 수단이 아니라 검색 제어 수단이다.
Milvus에서 파티션은 하나의 컬렉션 내부를 논리적으로 나누는 구조이다.
파티션은 다음을 결정한다.
파티션은 etcd 메타데이터에만 기록된다.
객체 스토리지 구조를 분리하지 않는다.
같은 컬렉션이라면 모든 데이터는 동일한 MinIO 버킷에 저장된다.
Milvus의 실제 저장 단위는 세그먼트이다.
각 세그먼트는 특정 파티션에 소속된다.
즉, 파티션은 세그먼트를 묶는 논리적 레이블이다.
파티션 조회는 검색 대상 세그먼트를 줄이는 작업이다.
쿼리 요청 시 특정 파티션을 지정하면, QueryNode는 해당 파티션에 속한 세그먼트만 로드한다.
이는 곧 다음을 의미한다.
파티션은 검색 파이프라인의 첫 단계에서 적용되는 제약 조건이다.
필터 조회는 파티션 조회와 완전히 다른 단계에서 동작한다.
필터는 세그먼트 내부의 데이터 선별 조건이다.
예를 들어 특정 필드 값 조건을 만족하는 벡터만 대상으로 한다.
필터는 다음 순서로 적용된다.
즉, 필터는 이미 로드된 데이터에 대해 적용된다.
두 조회 방식의 가장 큰 차이는 적용 시점이다.
| 구분 | 파티션 조회 | 필터 조회 |
|---|---|---|
| 적용 시점 | 검색 전 | 검색 중 |
| 영향 범위 | 세그먼트 선택 | 엔트리 선택 |
| 저장 구조 영향 | 없음 | 없음 |
| 목적 | 탐색 공간 축소 | 조건 만족 데이터 선택 |
파티션은 “어디를 볼 것인가”를 결정한다.
필터는 “그 안에서 무엇을 볼 것인가”를 결정한다.
Milvus 파티션을 운영체제 파티션처럼 사용하려는 접근은 위험하다.
Milvus 파티션은 검색 전략의 일부로 설계되어 있다.
저장 전략의 일부로 오해하면 잘못된 구조가 된다.
Milvus 파티션은
디스크를 나누는 개념이 아니라
검색 대상 범위를 줄이기 위한 논리적 장치이다.
파티션 조회는 검색 파이프라인의 입구에서 작동하고,
필터 조회는 파이프라인 내부에서 작동한다.
이 둘을 구분하지 못하면
“왜 파티션을 나눴는데도 느린가”라는 질문에 답할 수 없다.