オブジェクトストレージとデータレイク
データレイクの土台であるS3的オブジェクトストレージが、なぜファイルシステムと違う挙動をするのかが腑に落ちます。結果整合性・リスト操作・小ファイル問題・強整合性化までを、分析基盤の設計視点で原理から押さえられます。
- 1.オブジェクトストレージはPUT/GET/DELETE/LISTだけを持つ巨大なキー・バリューストアで、ディレクトリもリネームも無い。キーの区切り文字(/)とプレフィックスで階層を演出しているだけで、パスの見た目に反してフラットな名前空間である。
- 2.従来S3はGET-after-PUTのみ強整合で、更新・削除の反映やLISTは結果整合だった。2020年末以降のS3は読み取り/リストとも強整合になったが、複数オブジェクトをまたぐアトミック性やロックは依然として存在せず、分析基盤側で整合性を補う必要がある。
- 3.1リクエストのレイテンシが大きくスループット課金される特性上、小ファイルの大量生成(LIST爆発・オープンコスト)が性能を殺す。テーブルフォーマット(Iceberg/Delta/Hudi)はファイル一覧をメタデータ側に持ち、LISTと結果整合の穴を回避してACIDを後付けする。
データレイクの土台は「ファイルシステムではない」
データレイクは、S3・GCS・Azure Blob(ADLS)のようなオブジェクトストレージの上に成り立っています。s3://lake/events/dt=2026-06-21/part-00000.parquet のようなパスを見ると、つい普通のファイルシステムだと思ってしまいます。しかしこの前提のまま設計すると、結果整合の罠・LISTの遅さ・小ファイルの爆発で分析基盤は確実に詰まります。オブジェクトストレージはファイルシステムに見せかけた、まったく別物のキー・バリューストアだからです。
本記事は、ローカルディスクやRDBのストレージエンジンの話ではなく、ペタバイト級の分析データを安く大量に置くための分散オブジェクトストレージと、その上に**分析基盤(データレイク/レイクハウス)**を築くときの整合性・性能の勘所を原理から扱います。ストレージ内部のB+木やLSMではなく、「分散ストレージが提供する保証」と「分析エンジンがそれをどう使うか」の境界が主題です。
S3的なオブジェクトストレージのAPIは、本質的に次だけです。PUT(キーに対しオブジェクト全体を書く)、GET(キーの中身を読む。範囲指定のレンジGETも可)、DELETE(キーを消す)、LIST(プレフィックスに一致するキーを列挙する)。ここに**追記(append)**も、**部分更新(in-place update)**も、リネーム(rename)も、ディレクトリも、ロックもありません。オブジェクトは常に全体を置き換える単位であり、名前空間は階層ではなくフラットなキーの集合です。
フラットな名前空間:ディレクトリは「/」の錯覚
最初の落とし穴がディレクトリの不在です。オブジェクトストレージのバケットは、events/dt=2026-06-21/part-00000.parquet という長い1本のキーを持つオブジェクトが並ぶだけのフラットな集合です。events/ や dt=2026-06-21/ という中間ディレクトリは、実体としては存在しません。あるのは区切り文字(慣習的に /)を含んだキー文字列だけで、階層は人間とツールがそう解釈しているにすぎません。
このフラットさが挙動に直結します。リネームがアトミックでないのはこのためです。dir_a/ を dir_b/ に名前変更するには、dir_a/ プレフィックスの全キーをLISTし、各オブジェクトを新キーへコピー(サーバー内コピーでも1オブジェクトずつ)し、旧キーをDELETEする——という多数の独立操作の集合でしかありません。途中で失敗すれば半分だけ移った中間状態が残ります。ディレクトリ単位のmvを一発でアトミックに行う手段は無い、というのがオブジェクトストレージの根本制約です。
初期のHive/MapReduce流の書き込みは、まず一時ディレクトリ(_temporary/)に出力し、成功したら最終ディレクトリへリネームしてコミットする設計でした。これはリネームがアトミックなHDFS(真の分散ファイルシステム)では成立します。しかしオブジェクトストレージ上ではリネームが「LIST+大量コピー+削除」に化けるため、遅く・非アトミックで・結果整合の影響も受ける。S3上でこの方式が破綻したことが、後述するテーブルフォーマットが「ファイル一覧をメタデータで管理する」方向へ進んだ大きな動機です。
結果整合性から強整合性へ
分散オブジェクトストレージは、可用性と耐久性のためデータを複数ノード・複数拠点に複製します。ここで避けられないのが整合性モデルの問題です。ある書き込みが、直後の読み取りやリストにいつ反映されるかという保証の話です。
歴史的にAmazon S3は長く**結果整合性(eventual consistency)**を採っていました。おおまかに、
- 新規キーの
GET-after-PUT(初めて書いたオブジェクトの読み取り)=強整合(read-your-writes)。 - 既存キーの上書き(overwrite PUT)や
DELETE後のGET=結果整合。更新前の古い版や、消したはずのオブジェクトが一時的に見えることがある。 LIST=結果整合。たった今PUTしたキーが一覧にまだ現れない、あるいは消したキーがまだ残って見えることがある。
分析基盤にとって最も厄介なのが最後のLISTの結果整合でした。書き込みジョブが出力した新しいParquetを、直後の読み取りジョブがLISTしても取りこぼす——つまり「書いたのに読めない」欠損が起きうる。これを回避するために、かつては外部に「本当に存在するファイルの一覧」を別管理する仕組み(S3Guardのような整合性レイヤ)まで必要でした。
2020年12月以降、Amazon S3はすべての読み取り・LISTを強整合(strong read-after-write consistency)に変更しました。新規・上書き・削除いずれの後でも、成功した書き込みは次のGET/LISTから必ず反映されます(追加料金・性能低下なし)。GCSやAzure Blobも同様に強い整合性を提供します。ただし勘違いしてはならないのは、これは1オブジェクト単位の可視性の保証にすぎない点です。「複数オブジェクトをまとめて一貫して切り替える」ことは、強整合になっても依然として保証されません。
なぜ「1オブジェクトの強整合」だけでは足りないのか。分析テーブルは多数のファイルの集合で1つの論理状態を表すからです。「10個のParquetを追加し、3個を削除扱いにして新しいテーブル状態にする」という操作は、個々のPUT/DELETEがそれぞれ強整合でも、その集合全体が同時に切り替わる保証がありません。途中まで反映された中間状態を他のリーダーが観測しうる。強整合化は結果整合の欠損の悩みを消しましたが、マルチオブジェクトのアトミック性という別次元の問題は手つかずのまま残ります。
リスト操作とプレフィックス:フラットさが性能を決める
LISTは分析基盤で最も酷使され、最もボトルネックになりやすい操作です。仕組みを押さえておく必要があります。
LISTは「あるプレフィックスで始まるキー」を返しますが、一度に返る件数には上限(S3では1回あたり最大1000件)があり、続きは継続トークン(continuation token)でページングして繰り返し取得します。つまりプレフィックス配下に100万オブジェクトあれば、1000件×1000往復のAPI呼び出しが要る。オブジェクトストレージは1リクエストのレイテンシが数ミリ〜十数ミリ秒あるため、この往復数がそのままメタデータ取得の待ち時間として効きます。テーブルを開くたびに全ファイルをLISTしていては、実データを1バイト読む前に時間を浪費します。
LIST に区切り文字(delimiter=/)を渡すと、直下の擬似ディレクトリだけを共通プレフィックス(CommonPrefixes)として返せます。これでディレクトリ風の走査ができますが、あくまでキー文字列の接頭辞照合であって、木構造の索引を辿っているわけではありません。したがって「dt=2026-06-21/ 配下だけ」を絞るのは速くても、part-* の中の特定条件にプレフィックスと無関係な述語(例:ある列の値域)でファイルを絞ることはLISTだけでは不可能です。値でファイルを枝刈りするには、別に統計メタデータが要ります。
ここでプレフィックス設計が性能を左右します。歴史的にS3の内部パーティション(リクエストをさばく単位)はキーのプレフィックスに基づいて自動分割されてきました。かつては先頭が単調増加する日付やIDだと、リクエストが特定パーティションに集中してスループット上限(当時プレフィックス毎に秒あたり数千リクエスト規模)に張り付くため、先頭にハッシュを付けてキーを散らす設計が推奨されました。現在のS3はプレフィックスごとに自動スケールし高いリクエストレートに適応しますが、キーの散らばりがスループットに影響しうるという原理自体は、分散キー・バリューストア共通の性質として理解しておく価値があります。
小ファイル問題:オブジェクトストレージ特有の税金
分析基盤で繰り返し語られる小ファイル問題は、オブジェクトストレージの特性から直接生まれます。核心は「1オブジェクトあたりの固定コストが大きい」ことです。
- リクエスト単位の課金とレイテンシ:
GET/LISTはリクエスト数で課金され、1リクエストごとに往復レイテンシが乗る。ファイル数がそのままリクエスト数として効くため、数KBのファイルが100万個あると、実データ量に不釣り合いなAPIコストと待ち時間が発生する。 - オープン・メタデータ読みの固定費:Parquetのようなフォーマットはファイルごとにフッタのスキーマ・統計を読む必要があり、ファイルが小さいほど実データ対メタデータの比率が悪化する。
LISTの肥大:ファイル数が増えれば、テーブルを開くためのLIST往復数も比例して増える。
# 同じ 1 TB を置く二つのレイアウト
小ファイル: 5,000,000 個 × 200 KB
→ LIST は 5000 往復、開くのに 500 万回のフッタ読み、リクエスト課金も 500 万回
適正サイズ: 4,000 個 × 256 MB
→ LIST は 4 往復、フッタ読みも 4000 回、リクエスト課金は桁違いに小さい
小ファイルは、ストリーミング取り込み(マイクロバッチが毎回小さなファイルを吐く)や、細かすぎるパーティション設計(/data-engineering/partitioning-bucketing/ で扱う組合せ爆発)で量産されます。対策の定石がコンパクション(多数の小ファイルを読み直して数百MB級の少数ファイルへ束ね直す)で、パイプラインの/data-engineering/etl-elt-pipelines/ に定期処理として組み込みます。目安として1ファイル数百MB程度・均一が、オープンコストの償却と並列スキャンの両立を取りやすいサイズです。列指向ファイルの内部構造(行グループが並列スキャン単位になる点)は /data-engineering/columnar-parquet-orc/ で詳述しています。
オブジェクトストレージの保存料金はバイト単価なので、小ファイルでも保存費はさほど変わりません。だから軽視されがちですが、痛いのはリクエスト課金と実行時間です。テーブルが育つほどファイル数は単調に増え、クエリのLISTとオープンが重くなり、コンパクションを回さないと遅延とコストが複利で悪化します。取り込み時のバッチサイズ調整と定期コンパクションを最初から運用に入れるのが鉄則です。
テーブルフォーマットが整合性を補う仕組み
ここまでの制約——マルチオブジェクトのアトミック性が無い・リネームが非アトミック・LISTが遅い(かつては結果整合だった)——を分析基盤側で埋めるのが、テーブルフォーマット(Apache Iceberg、Delta Lake、Apache Hudi)です。発想を一言でいえば、「どのファイルがいまのテーブルの中身かを、LISTではなくメタデータで宣言する」ことです。
| オブジェクトストレージ単体の弱点 | テーブルフォーマットによる補い方 |
|---|---|
| どのファイルが最新テーブルかを LIST でしか知れない | スナップショットのメタデータに正確なファイル一覧を持ち、LIST に依存しない |
| 複数オブジェクトを同時に切り替えられない | 最新メタデータを指す一点を CAS で切り替え、原子性をその一点に集約する |
| 書き込み途中の中間状態が見える恐れ | 読み取りは常に単一スナップショットに固定し、スナップショット隔離を得る |
| リネームでコミットできない | ファイルは追加のみ(不変)、コミットはメタデータのポインタ差し替え |
| LIST が遅く述語で枝刈りできない | マニフェストの列統計でファイル単位のプルーニングを行う |
肝はデータファイルを不変(immutable)に保ち、可変なのはメタデータだけにする分離です。書き込みは既存ファイルを一切書き換えず、新しいデータファイルを追加し、それらを含む新しいスナップショットを作り、テーブルが指す最新ポインタをアトミックに1点だけ差し替える。この一点の切り替えをcompare-and-swap(CAS)(「自分が読んだ最新がNのままならN+1へ進める」を条件付きで行う)にすることで、複数ライターの競合を楽観的並行制御で捌き、スナップショット隔離のACIDをオブジェクトストレージ上に後付けします。
そしてファイル一覧を自前のメタデータに持つため、LISTの遅さと(かつての)結果整合の両方を回避できます。かつて結果整合のLISTで「書いたのに読めない」欠損が起きたのは、正解の一覧をストレージのLISTに頼っていたからで、メタデータに正確な一覧を書いておけば、リーダーはそのメタデータだけを読めばよい。強整合化された現在のS3でも、マルチオブジェクトのアトミック性はストレージが保証しない以上、この「メタデータへの一点集約」は依然として必要です。テーブルフォーマット内部のスナップショット構造・CAS・プルーニングの詳細は /data-engineering/lakehouse-iceberg-delta/ で掘り下げています。
- 「S3はファイルシステムか」:いいえ。
PUT/GET/DELETE/LISTだけのフラットなキー・バリューストアで、ディレクトリ・追記・リネーム・ロックが無い。パスの/は区切り文字による階層の演出。 - 「S3の整合性はどう変わったか」:かつては新規
GET-after-PUTのみ強整合で上書き・削除・LISTは結果整合。2020年末以降は読み取り・LISTとも強整合。ただしマルチオブジェクトのアトミック性・ロックは今も無い。 - 「小ファイル問題の原因」:保存料金ではなく、リクエスト課金・往復レイテンシ・フッタ読みの固定費・
LIST肥大。対策はコンパクションと適正サイズ化(数百MB・均一)。 - 「テーブルフォーマットがオブジェクトストレージを補う点」:ファイル一覧をメタデータで宣言し
LISTと結果整合の穴を回避、最新ポインタの一点CASで原子性を、スナップショット隔離で分離性を実現する。
まとめ
- データレイクの土台であるオブジェクトストレージ(S3/GCS/Azure Blob)はファイルシステムではなく、
PUT/GET/DELETE/LISTだけを持つフラットなキー・バリューストア。ディレクトリ・追記・リネーム・ロックは無く、パスの/は階層の演出にすぎない。 - リネームは「
LIST+大量コピー+削除」に化けるため非アトミック。ディレクトリ移動でコミットする古い手法がS3で破綻したことが、テーブルフォーマット台頭の一因。 - S3は当初新規
GET-after-PUTのみ強整合(上書き・削除・LISTは結果整合)で、LISTの結果整合が「書いたのに読めない」欠損を生んだ。2020年末以降は読み取り・LISTとも強整合になったが、複数オブジェクトをまたぐアトミック性・ロックは依然として無い。 LISTは最大件数ごとのページングで往復し、プレフィックスは文字列の接頭辞照合。値による枝刈りはLISTだけでは不可能で、別途統計メタデータが要る。キーの散らばりがスループットに影響しうる。- 小ファイル問題の正体は保存料金ではなく、リクエスト課金・レイテンシ・フッタ読み・
LIST肥大という固定費。コンパクションと適正サイズ化(数百MB・均一)が要。 - テーブルフォーマット(Iceberg/Delta/Hudi)は、ファイル一覧をメタデータで宣言して
LISTと結果整合を回避し、最新ポインタの一点CAS+楽観的並行制御+スナップショット隔離で、オブジェクトストレージ上にACIDを後付けする。
データ工学 Article
オブジェクトストレージとデータレイクを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
オブジェクトストレージ
比較で見る軸
難易度: advanced / カテゴリ: データ工学 / タグ数: 6
導入後に効く点
従来S3はGET-after-PUTのみ強整合で、更新・削除の反映やLISTは結果整合だった。2020年末以降のS3は読み取り/リストとも強整合になったが、複数オブジェクトをまたぐアトミック性やロックは依然として存在せず、分析基盤側で整合性を補う必要がある。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- データ工学
- タグ数
- 6
判断チェックリスト
- 自社の用途が「オブジェクトストレージ / データレイク」に近いか確認する。
- 強みである「オブジェクトストレージはPUT/GET/DELETE/LISTだけを持つ巨大なキー・バリューストアで、ディレクトリもリネームも無い。キーの区切り文字(/)とプレフィックスで階層を演出しているだけで、パスの見た目に反してフラットな名前空間である。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。