Apache Iceberg Meetupのセッション「Lance Integration with Apache Iceberg - Latest Progress」をまとめます。
- スピーカー
- マルチモーダルAIが突きつけるデータ規模の課題
- データウェアハウスからマルチモーダルレイクハウスへ
- 単一データセットで全モダリティを扱うLanceの構想
- ランダムアクセスに最適化したLanceファイルフォーマット
- Row Groupを排した格納レイアウトと高速リトリーバル
- インデックスを第一級に据えたテーブルフォーマット
- バックフィルを回避するData Evolution
- Lakehouseの抽象化を担うカタログとLanceDBとの違い
- ファイルフォーマット統合の検証とフォーマット2.1
- FragmentをParquet相当に近づけるインデックスとクエリ実行
- Icebergカタログ上でLanceテーブルを扱う
- まとめ
スピーカー
このセッションは、LanceDB の Jack Ye 氏によって行われました。Jack 氏は Apache Iceberg の PMC メンバーであり、Apache Polaris の PMC メンバー兼メンテナーとして、複数のオープンソースプロジェクトに携わっています。
LanceDB に参加する前は Amazon のエンジニアとして、Athena や EMR におけるオープンテーブルフォーマット連携の技術リードを務め、SageMaker Lakehouse や S3 Tables の立ち上げを主導してきました。本セッションは、約半年前に同じ題目で行った発表の続編にあたり、当時は構想に留まっていた Lance と Iceberg の統合がこの半年でどこまで形になったかを報告する内容です。
マルチモーダルAIが突きつけるデータ規模の課題

本セッションは大きく二つの軸で構成されています。前半では Lance というデータフォーマットの背景を、ファイルフォーマット・テーブルフォーマット・カタログの三層に分けて押さえます。後半は Iceberg との統合に向けてこの半年で進んだ内容を、同じ三層の切り口で報告する流れです。半年前の発表時点では構想だけだった統合が、実装としてどこまで動くようになったかが主題になります。

Lance が「なぜもう一つ別のフォーマットを作るのか」という問いの答えは、いま AI が直面しているマルチモーダルデータの規模に行き着きます。マルチモーダルデータはテーブルデータよりはるかに大きく、行あたりのサイズが桁違いに膨らんでいきます。
スライドはこの膨張を具体的な数字で示しています。テーブルデータは1行あたり約145バイトです。ここに embedding(ベクトル表現)を加えると1行あたり約4KBになり、25倍に増えます。画像を加えると約80KBで500倍、動画に至っては約50MBで350,000倍にまで跳ね上がります。Iceberg でペタバイト級のテーブルといえば巨大な部類ですが、それはテーブルデータが小さいからこそ成立する規模感です。embedding や画像、動画を載せた途端にテーブルは破綻的に膨れ上がります。
さらに、ML/AI のワークロードは従来のデータレイクが得意とする集計ではなく、特定の行へのアクセス、つまり retrieval を重視します。学習時のシャッフル、推論時の top-k 検索、そしてモデルのデバッグのためのリプレイがその例です。こうした特定行へのランダムアクセスは、集計向けに最適化された既存フォーマットには向きません。現状は既存システムを回避策で使い回している状態で、私たちはマルチモーダル AI の時代によりよく適合する解が作れると考えています。
データウェアハウスからマルチモーダルレイクハウスへ

「Frictionless AI Infrastructure(摩擦のない AI インフラ)」と題した図は、左右の対比で全体像を示しています。左側に置かれた Traditional Data Lake(従来のデータレイク)には、上から Full Text・Similarity・SQL という 3 種類のクエリ要求が伸びており、それぞれを ElasticSearch、Vector DB、Postgres という別々のシステムが受け持っています。さらにデータレイクの下には、Training Lake(TFRecord / WebDataset)、Image/Video on S3、Eval DataWarehouse というデータの置き場が分かれ、その先で PyTorch、Ray.data、Eval in Notebook、Spark/Trino といったエンジンが消費する形になっています。
この構図は、10 年前のデータウェアハウスとデータレイクの関係をそのままなぞっています。データがウェアハウスに入ると、用途ごとに別ソリューションを用意せざるを得ず、データサイロが生まれていました。右側の Multimodal Lakehouse では、Hybrid(Vector + Full Text + SQL)という単一の入口から PyTorch、Eval in Notebook、Spark/Trino、Ray.data へとつながり、同じデータを複数のエンジンが直接扱える姿を描いています。
データレイクへの移行が進み、Iceberg をはじめとするオープンテーブルフォーマットでデータを管理し、その上で任意の分析エンジンや BI エンジンを動かせるようになりました。集計やレポーティングの世界では、この分離によってサイロの問題はおおむね解消されています。
ところが ML/AI のワークロードに目を移すと、この道のりはまだ完結していません。データレイク化で得られたはずの「データを一箇所に置き、エンジンを自由に選ぶ」という性質が、AI 側ではまだ実現できていないのです。
AI のワークロードでサイロが残る理由は、検索の要件にあります。全文検索(full-text search)で十分な性能を出そうとすれば、データを丸ごと Elasticsearch に取り込む必要があります。ベクトル検索(similarity search)をやりたければ、専用のベクトルデータベース製品を別途選ぶことになります。
つまり用途ごとにデータを別システムへコピーし続ける構造が温存され、結局はデータサイロを再生産してしまいます。図の左側で ElasticSearch・Vector DB・Postgres がそれぞれ独立しているのは、まさにこの状況を表しています。
私たちが思い描くのは、マルチモーダルなデータを一つのオープンなフレームワークで支える姿です。データは単一のソリューションの中に置かれ、異なるエンジンがそこへアクセスして AI のワークロードを満たせるようにします。
図の右側の Multimodal Lakehouse がその到達点です。Vector・Full Text・SQL のハイブリッドなクエリを単一の入口で受け止め、その同じデータを PyTorch や Spark/Trino、ノートブック上の評価などが直接消費できます。検索のためにデータを別システムへ移し替える必要をなくし、サイロを解消することが Lance の狙いです。
単一データセットで全モダリティを扱うLanceの構想

Multimodal Lakehouse の構想を具体化すると、ひとつの LanceDB データセットが多様なカラムを同居させ、それぞれに適した検索方式を同じデータに対して直接適用する形になります。図には画像データを扱う一例が示されています。package_id(int64)、image_id(text)、NSFW_score(float)といった構造化カラムに加えて、image_caption(text)、そして vae_embedding と text_embedding という2種類のベクトルカラムが、1枚のテーブルの中に並んでいます。
この単一データセットに対して、用途の異なる3つのクエリが発行されています。SELECT * FROM images WHERE country = "KR" and NSFW_score > 0.90 のような条件絞り込みは通常の SQL クエリです。table.search("woman dancing").limit(10) はキャプションのテキストに対する全文検索になります。そして table.search(Vector).limit(10) は ANN(Approximate Nearest Neighbor、近似最近傍)によるベクトル検索です。ANN はベクトル空間上で近いものを効率よく探す手法で、類似画像や意味的に近い文書を引くときに使われます。
それぞれのカラムは性質が大きく異なります。NSFW_score のような数値や image_id のような短いテキストは、SQL のフィルタリングに向いた構造化データです。image_caption は captioning サービスや大規模言語モデルが生成した長文のテキストで、全文検索の対象になります。同じデータセットの中で、構造化フィルタと全文検索を同時に成立させられる点がここでの主張です。
ベクトルカラムも一様ではありません。vae_embedding と text_embedding のように、異なる embedding モデル(埋め込み生成器)から作られた複数のベクトルが共存します。VAE 由来の画像埋め込みとテキスト埋め込みのように、生成元の違うベクトルをそれぞれ別カラムとして保持し、用途に応じたベクトル検索を使い分けられます。
構造化フィルタ、全文検索、ベクトル検索という性質の違うアクセスを、オープンな単一のソリューションでまとめて扱えること。これが私たちの目指す姿です。
それを支える Lance は、ランダムアクセスに最適化されたファイルフォーマットです。集計のためのスキャンを前提とする従来のフォーマットとは設計の出発点が異なり、特定の行を高速に引き出す retrieval を中心に据えています。マルチモーダルなカラムを1枚のデータセットに収め、複数の検索方式を同じ実体に直接向けられるのは、このファイルレベルでの設計に拠っています。
ランダムアクセスに最適化したLanceファイルフォーマット

Lance はファイルフォーマットとテーブルフォーマットの両面を持ちます。ファイルフォーマット側はランダムアクセスに最適化されており、スキャン速度よりも特定行への高速な到達を狙う設計です。これは Iceberg が標準で用いる Parquet が、オーバーラップやスキャンのスループットに最適化されているのとは方向性が異なります。
ファイルフォーマットとしての特徴は具体的です。高速スキャンのためのカラムナ構造を保ちつつ、O(1) でのランダムアクセスを可能にするエンコーディングを採用しています。圧縮は FSST、Dictionary、RLE、Structure Encodings といった方式に対応します。深くネストしたフィールドにも 1〜2 回の I/O で到達でき、大きな blob 向けの最適化を備えます。Parquet にある Row Group の概念は持たず、オブジェクトストア上での利用を前提に設計されています。
テーブルフォーマット側は二次元ストレージによるゼロコピーのデータ進化、バージョニングとタイムトラベル、Shadow Clone やブランチ・タグ付けに対応します。Vector・full text・scalar・JSON・Geo・Zone Map・bloom filter といった二次インデックス、LSM 構造、Apache Arrow API、ランダムアクセスと大きな blob を見据えたグローバル I/O 実行計画、行レベルでの競合解決も備えます。
ランダムアクセス性能への投資が、ベクトル検索やフルテキスト検索といった検索ワークロード有効です。テーブルが大きくなる点では Iceberg と似た性質を持ちますが、Lance にはデータレイク上で機械学習・AI ユースケースに寄せたネイティブ機能があります。二次元ストレージやゼロコピーのデータ進化はその代表で、列を追加・差し替えしてもデータ全体を書き直さずに済む仕組みです。

Lance はすでに広く使われており、AI 領域で最もトレンドのフォーマットの一つになっています。AI 向けに独自のデータレイク基盤を構築する企業、データインフラのベンダー、そして基盤モデルを手がける企業が利用しています。スライドには Netflix、Databricks、Uber、World Labs、LUMA AI、Midjourney、Runway、character.ai、Tubi、AWS、ByteDance、Harvey、Exa、Nomic、Bosch、HEX、rerun.io、TwelveLabs が並びます。マルチモーダル AI の世界で、こうした顔ぶれが共通の基盤を育てているという形です。

ファイルフォーマットの設計目標は高速なランダムアクセスに集約されます。Lance が保証するのは到達コストの上限です。固定長フィールドであれば、struct のネストや nullability がどれだけ深くても 1 IOP で読み出せます。可変長フィールドの場合は、struct ネスト・list ネスト・nullability の深さによらず 2 IOP で済みます。
これを実現する設計判断として、チャンク単位で扱う必要があり中身が不透明(opaque)になりうる圧縮エンコーディングも許容しています。そして大きく取り除いたのが Row Group です。Parquet の Row Group は行をまとめたブロックで、特定の 1 行を取り出すにはブロック全体の解凍が絡んで余分な I/O を招きます。これを廃して構造を単純化することで、ランダムアクセスの素直な性能を引き出しています。
Row Groupを排した格納レイアウトと高速リトリーバル
Row Group が性能を壊す理由

Parquet をはじめとする列指向フォーマットは、データを Row Group という単位で区切って格納します。これがマルチモーダルデータでは性能を損なう要因になります。マルチモーダルデータは boolean の 1 バイトから数十 GB の動画まで、列ごとのサイズが極端に違うためです。
図はその矛盾を二つの「悪い選択肢」として示しています。Row Group を 1 GiB 級に大きく取ると、書き込み時にメモリ上へ大量のデータを抱え込むため OOM を起こしやすく、読み出し時も並列度が稼げません。逆に Row Group を小さく刻むと、画像や動画のような幅の広い(wide)列は適切に格納できる一方、boolean のような狭い(narrow)列は極端に小さなページ(runt page)に分割され、読み出しのたびに細切れの I/O が発生して効率が落ちます。どちらに振っても、特定行を取り出すリトリーバル用途には向きません。
Row Group をなくしたレイアウト

そこで Lance のファイルフォーマットは Row Group を持ちません。書き込み時は列ごとにデータページを書き出すだけで、ページ数は列によって異なってよい構造になっています。幅の広い列はページが多くなり、狭い列は少なくなるという形です。エンコーディングもページ単位で変えられます。
ファイル末尾には Column Metadata と Footer が置かれ、Column Metadata は各データページ中のバッファ位置を指し示します。Footer のメタデータテーブルは Column Metadata ブロックを指すため、必要な列だけを取り出す true column projection が成立します。スキーマや列全体の辞書といったメタデータバッファはファイル内のどこにでも配置できます。Row Group という固定境界がない代わりに、テーブル側でアライメントを取ることで書き込みを速くし、ランダムアクセスも高速化しています。

このファイルフォーマットの設計は、論文「Lance: Efficient Random Access in Columnar Storage through Adaptive Structural Encodings」としてまとめられています(Weston Pace ほか、2025年4月)。論文のアブストラクトは、AI ワークロードがシーケンシャルアクセスとランダムアクセスの両方を要求すること、そして NVMe ベースのストレージがキャッシュ層として登場している状況を背景に挙げています。モデル学習や特徴抽出といったタスクでは数万列に及ぶこともあり、従来のフルスキャン最適化フォーマットだけでは対応しきれないという問題意識です。Adaptive Structural Encoding という名前は、列の構造に応じてエンコーディングを切り替えるこの設計を指しています。
列ごとに差し替えられるエンコーディング

Lance はエンコーディングを Pluggable にしており、列の性質に応じて格納方式を選べます。これが GPU への画像投入のような場面で効果的です。学習のために画像を取り込む際、より速くデータを引き出してアクセスできるようになるためです。
図は大きなバイナリ列の扱い方を示しています。動画のような巨大なオブジェクトは BlobFile として扱い、uri・pos・length を持つハンドルが実体の格納位置とサイズを指す形で、seek と read でストリームとして読めるようにしています。右側のコードは、lance.dataset から take_blobs で video 列の Blob を取り出し、PyAV(import av)でデコードしながら指定区間のフレームだけを表示する例です。skip_frame でキーフレーム以外を飛ばし、time_base で開始・終了時刻を算出して seek で頭出しし、end_time を超えたフレームでループを抜けています。巨大な動画列であっても、必要な区間だけをランダムアクセスで取り出せることを示しています。
インデックスを第一級に据えたテーブルフォーマット

Lance のテーブルフォーマットは、設計の方向性としては Iceberg よりも Delta Lake に近いものです。狙いは Delta と同じ種類のトランザクション性をテーブルの上に乗せることにあります。
データセットのレイアウトを見ると、ルート配下に複数のディレクトリが並びます。data/ には実データの *.lance ファイル、_versions/ にはスナップショットを表す *.manifest、_indices/ には UUID ごとの index.idx、_deletions/ には削除を記録する *.{arrow,bin} ファイルが置かれます。さらに _transactions/ がトランザクション記録を、_refs/ が tags と branches を保持し、tree/ 以下にブランチごとのデータとバージョンがぶら下がる構造です。ブランチやタグをサポートする点も Iceberg/Delta と同様の発想です。
論理構造としては、Manifest がスキーマ(col1 string, col2 string, col3 int)と Transaction File を指し、複数の Fragment と各カラムの Index File を束ねます。Fragment は Iceberg や Delta でいうデータファイルに相当する単位ですが、内部で列ごとに別々のデータファイルへ分けて格納でき、Deletion File を伴う点が異なります。この「列を別ファイルに分ける」構造が、後述する ML 学習向けの機能を支えます。
私たちがこのフォーマットで重視したのは、ローカルのベクトル検索や全文検索を確実に速くすることでした。そのためにインデックスをフォーマットの中で第一級の存在として扱っています。インデックスの定義はすべて spec に明記されており、Index File はテーブルのエコシステムの正式な構成要素です。
もう一つの工夫が Fragment です。Fragment は Iceberg や Delta のデータファイルに似た単位ですが、同じ行集合に属する異なるカラムを別々のファイルへ分けて格納できます。図でも col1 のデータファイルと、col2・col3 をまとめたデータファイルが一つの Fragment 内に同居しています。列を分割して書けることで、ML 学習で必要になる柔軟なデータ進化や部分的な読み出しが可能になります。
ファイルとしてのインデックス

Lance では「インデックスは値から行アドレスへのマッピングを保持する、もう一組のファイルにすぎない」という考え方を取ります。すべてが spec に基づいて定義され、専用ファイルとして表現されるため、インデックス自体がオブジェクトストアにネイティブな存在になります。更新は Copy-on-write と Delta の組み合わせで扱います。
対応するインデックスは用途ごとに揃っています。ベクトル検索向けには IVF-PQ/SQ/RQ、SPFlash、HNSW といった近似最近傍探索のアルゴリズムを用意しています。全文検索には BM25 によるスコアリング、JSON、各種 tokenizer を備えます。スカラ値には BTree、Bitmap、Label list に加え、Zone Map と Bloom Filter を持ち、Geo Index も対象に入っています。
スライドのコード例では、lance.dataset("sift.lance") を開き、to_table に columns=["item_id", "revenue"] と nearest={"column": "vector", "q": samples[0], "k": 10} を渡して、ベクトルカラムへの top-k 検索結果を pandas で受け取っています。指定したカラムだけを射影しつつ、ベクトル検索を一度の呼び出しで実行できる形です。
バックフィルを回避するData Evolution

Lance のフラグメント構造は、列を増やす操作をデータの書き換えなしに実現します。これが「old-school なスキーマ進化」を超える Data Evolution の中身です。
従来の Iceberg でも列の追加そのものはできます。新しい列を加えると、それ以降に書き込むデータファイルにその列が含まれ、既存データではその列が NULL として表示される形になります。ところが ML/AI のワークロードでは、列を増やす行為は多くの場合「特徴量(feature)を追加する」ことを意味します。新しい特徴量は既存の全行に対して値を埋める必要があるため、既存データをバックフィルしなければなりません。Iceberg のデータファイルは1ファイルに全列が同居しているので、特徴量を1つ足すたびにテーブル全体を実質的に書き直すことになります。
Lance では、Fragment が同じ行集合の異なる列を別ファイルに分けて持てるため、新しい列は新しいファイルとして追記するだけで済みます。図では column a が file1(v1)と file2(v2)、column b が file3(v3)と file4(v4)に分かれ、操作順序は create → append → add column となっています。列の追加が事実上の追記になり、テーブル全体を実体化し直すバックフィルを回避できます。コード例の ds.add_columns(multiply_by_two) のように、既存列を入力にとる Python の UDF を渡せば、変換結果を新しい列として書き加えられます。Huawei はこの仕組みを広く使っています。
例: 動画処理パイプライン

動画から抽出したデータを Lance の AI データレイク上で段階的に加工していく例です。1つの素材から派生データを次々に列として積み上げていく流れがそのままパイプラインになります。
構成は3つのテーブルに分かれます。Table 1 は動画そのものと動画由来のデータで、Videos 列に加えて width・height・fps・duration・prompts を持ちます。ここから opencv や ffmpeg で画像を抽出し、Table 2 の Images とその派生データ(time、caption、depth map、embedding、thumbnail、tags など)が生成されます。さらに Table 3 では音声と音声由来のデータ(Audio、time、transcript、embedding)を扱います。図中のひし形は image2text、open cv、image embed、tagger、pillow といった変換処理を表し、矢印が列から列への派生関係を示します。
個々の変換は GPU を経由する Python 関数として書けます。言語モデルなどを通して得られる新しい派生結果は、いずれも新しい列として追加されていくだけです。
新しい特徴量がすべて新しい列の追記に帰着するため、パイプラインを拡張するたびに既存データを書き直す必要がありません。結果として学習用データの準備が速くなり、運用も簡単になります。
Lakehouseの抽象化を担うカタログとLanceDBとの違い

Lakehouse の三層のうち最上位にあたるカタログ層を、Lance では Lance Namespace として標準化しています。図の左側にはカタログやメタストア、メタレイクが並びます。AWS Glue、Apache Polaris、Apache Gravitino、Unity Catalog、Hive Metastore といった既存のメタデータ管理基盤です。右側には Lance のデータにアクセスするエンジン群が並びます。Apache Spark、Apache Flink、Ray、Trino、Apache Kafka などです。これらが Lance Namespace というインターフェースを介してつながる形になっています。
Lance Namespace は Iceberg のカタログインターフェースに近い発想で設計されています。違いは名前にも表れていて、対応すべき相手が必ずしもテーブルカタログとは限らないためです。Namespace はテーブルを格納する論理的な入れ物であればよく、より緩い抽象で多様なメタストアやエンジンを束ねられるようにしてあります。
Lance と LanceDB の違い

ここで頻繁に寄せられる質問が、Lance と LanceDB は何が違うのかというものです。整理しておくと、LanceDB にはクラウド製品としての側面と、オープンソースプロジェクトとしての側面の両方があります。名称が近いため混同されやすいので、四つのレイヤーに分けて区別します。
Lance Format は、マルチモーダルな AI データを格納するためのフォーマット標準そのものを指します。これに対して Lance SDK は、そのフォーマットの上で検索、クエリ、書き込み、メンテナンスといった機能を提供するライブラリです。GitHub の Lance リポジトリが対応するのはこの SDK にあたります。フォーマットの仕様と、その仕様を実装した SDK は別物だという整理です。
Lance SDK 本体は Rust で書かれており、Python や Java などへのバインディングを通じて各言語から利用できます。
Lance SDK と区別すべきなのが LanceDB のオープンソース版です。こちらは Lance の上に構築された、開発者にとって扱いやすい組み込み型のハイブリッド検索エンジンで、AI ユースケースやアルゴリズム、各種連携に焦点を当てています。機械学習エンジニアや AI 研究者向けに作られており、AI 関連の連携機能がここに集約されています。
LangChain や LlamaIndex、OpenAI、Gemini といった AI エコシステムとの連携は、いずれも LanceDB(オープンソース)側の世界に含まれます。
一方で、Spark のようなデータ処理エンジンから Lance のデータを扱う場合には、AI 連携を持つ LanceDB ではなく Lance SDK を直接使う形になります。データエンジニアリングの文脈では SDK、AI アプリケーション開発の文脈では LanceDB という使い分けです。
さらにその上位に、クラウドおよびエンタープライズ向けの LanceDB があります。こちらは分散型の検索エンジンであり、キャッシング、特徴量エンジニアリング、分析向けの SQL 機能などを備えています。LanceDB のオープンソース版は、このクラウド/エンタープライズ版に接続するクライアントとしても機能する位置づけです。
ファイルフォーマット統合の検証とフォーマット2.1

Lance を Iceberg のファイルフォーマットの一つとして使えるようにする構想は、2025 年の Iceberg Summit で提案されたものに端を発します。Iceberg が複数のファイルフォーマットをドライブできるようにするという内容で、Lance 側もこの提案に協力してきました。当時の時点では Lance はまだこのユースケースに十分対応できる状態ではありませんでした。
それでも統合の利点はすでに大きいと考えています。図にある BlobFile は、io.RawIOBase を継承し uri・pos・length を持つハンドルで、seek と read によって巨大バイナリ列の任意区間にアクセスできます。マルチモーダルデータの格納とランダムアクセスの両面で、Lance ファイルフォーマットを Iceberg に持ち込む意味があるという整理です。
ベクトル検索のような機能はテーブルフォーマットのレイヤーに属するため、ファイルフォーマット統合だけで得られる恩恵は限定的です。それでもコミュニティはこの一年、Lance を Iceberg のファイルフォーマットとして使う具体的なユースケースを探ってきました。その過程で有望だと分かったのが、マルチモーダルデータの格納と、ファイル単体でのランダムアクセス性という二つのケースです。
一つ目は画像や動画といったマルチモーダルデータの格納です。あるベンチマークでは、データを Lance ファイルフォーマットで保存して読み出す構成と、Parquet で保存する構成をいくつかの設定で比較しています。読み出し性能では Lance に保存したほうが大きく上回るという結果が得られています。
二つ目はファイルフォーマット単体でも得られるランダムアクセス性です。Lance はファイルレイヤーでランダムアクセスに最適化されているため、Iceberg からそのまま使う構成にも関心が集まっています。すでに複数の利用者が本番環境で統合を試している段階で、テストが成熟するにつれて Iceberg コミュニティからの参加もさらに増えると見込んでいます。

統合を進めるなかでいくつかのギャップも埋めてきました。これまで統合に使ってきたのはフォーマット 2.0 でしたが、寄せられたフィードバックを取り込んで 2.1 をリリースしています。
最も大きな指摘は圧縮率でした。Parquet と比べると Lance の圧縮率は見劣りします。これは Lance がランダムアクセスに最適化されており、強い圧縮がそれと相反するためで、デフォルトでは Parquet ほど強い圧縮をかけていません。2.1 ではこのギャップを埋め、ストレージコストを気にする場合や Parquet に近い容量を狙う場合には、いくつかのノブをチューニングして実現できるようにしました。

チューニングは Arrow スキーマのフィールドメタデータを通じて指定します。Lance は Arrow スキーマをそのまま使うため、pa.field の metadata に lance-encoding: プレフィックスのキーを書き込む形になります。
指定できる項目として、lance-encoding:compression で圧縮アルゴリズム(例: zstd)を、lance-encoding:compression-level で zstd の圧縮レベル(1〜22)を選びます。lance-encoding:blob を true にすると 4MB を超えるバイナリデータをチャンク分割ストレージとして扱い、lance-encoding:packed は struct のメモリレイアウト最適化を有効にします。ネスト構造のエンコーディング戦略は lance-encoding:structural-encoding で miniblock か fullzip を選べます。Parquet に近いストレージ効率が欲しければ、これらを調整すれば近づけられるという形です。
FragmentをParquet相当に近づけるインデックスとクエリ実行

Lance のテーブルフォーマットを Iceberg と並べると、Lance Manifest は Iceberg の metadata file と manifest list/manifest file の役割を担い、Fragment は最終的に data files にマッピングされます。Manifest がスキーマ・Transaction File・複数の Fragment・カラムごとの Index File を束ね、各 Fragment が同じ行集合の列を別ファイルに分けて Data File と Deletion File として持つ構造は、Iceberg のカタログ→metadata→manifest→data files という階層とよく対応します。この対応関係こそが、Lance を Iceberg のファイルフォーマットとして組み込む際の足がかりになります。

Iceberg と統合した場合の最大の魅力は、Fragment が Index File も指し示せる点にあります。Iceberg 側のメタデータが Lance の Fragment を参照すれば、そこから IVF_PQ や BTree、Bitmap、NGram、Label list といった各種インデックスを辿れるため、検索性能を保ったまま Iceberg のエコシステムに乗せられます。Iceberg の Reader/Writer API を介して Lance Manifest が持つインデックス群と Lance Fragment の双方を活用するという狙いです。

理想形は、Iceberg のカタログが持つ Avro ベースのメタデータ管理の利点を活かしつつ、その先で Lance の Fragment を参照する構成です。s3://bucket/lance_table の下に manifest v1 が schema・複数の Fragment・transaction file path・tag・config・indexes を抱え、Iceberg 側の Catalog から metadata.json、manifest-list.avro、manifest1〜10.avro を経由して各 Fragment の Data File 群・Deletion File・Index File に到達します。
Iceberg のメタデータは data files に相当する Fragment を指し、その Fragment から必要に応じて Index File を導出してクエリに使えます。実際にこの構成を本番で試している利用者も出始めています。
ZoneMap Index と Bloom Filter Index

この統合を一歩進めたのが、コミュニティからの貢献で追加された ZoneMap Index です。あわせて UX も整理した結果、Lance の Fragment は Parquet ファイルに格段に近い姿になりました。従来との違いを理解するには、それまで Lance が持っていたインデックスがどういう性質だったかを押さえる必要があります。
それまでの BTree や Bitmap といったインデックスは、いずれも値から該当行を正確に特定する exact index でした。検索でどの行を読むかをピンポイントで知りたい retrieval 用途には有効ですが、大量の行をスキャンしながら不要なデータブロックを読み飛ばしたい Iceberg 的な OLAP 用途には向きません。
ZoneMap Index は Parquet の Row Group 統計(各ブロックの min/max などのカラム統計)に相当する仕組みです。これは Parquet にもともと備わっていて OLAP に最適化された概念で、Lance にも持ち込まれました。Bloom Filter Index も同様に、特定の値が含まれるかどうかをブロック単位で素早く判定し、無関係なブロックの読み込みを省きます。
これらのインデックスはコミュニティのユーザーが統合を後押しするために加えたもので、OLAP 用途のギャップを埋めます。ZoneMap と Bloom Filter が加わったことで、Fragment は Parquet ファイルの良い縮約版として振る舞えるようになり、クエリ実行の面でも効果が及びます。
Spark のタスク分散 (Before / After)

従来の Spark 連携では、Driver から Fragment をそのまま Executor に配り、各 Executor 上の Lance Scanner(Java から Rust を呼び出す)が Fragment を読み、Index File を使ってプルーニングを行い、Arrow の RecordBatch に変換して Spark の Vectorized ColumnarBatch に渡していました。問題は、どのみちスキャンしなくてよい Fragment まで Executor に送られてしまう点です。プルーニングが Executor 側で起きるため、配ったあとで「この Fragment は不要だった」と分かるような無駄が生じます。

改善後は、ZoneMap が持つ zone ごとの min/max 統計を Driver 側で先にプリフェッチします。図では zone 1, zone 2, …, zone N の統計を Driver 上の Manifest と突き合わせ、Fragment と zone の組(Fragment 1 zone 2 のような単位)まで絞り込んでから Executor に渡しています。
このプリフェッチとプルーニングを、Fragment を Executor へ送る前の Driver 段階で済ませてしまう点が肝心です。無関係なブロックを抱えた Fragment を配らずに済むため、配布と実行の流れが Iceberg や Delta が得意とするクエリ実行に近づきます。
Icebergカタログ上でLanceテーブルを扱う

フォーマットの統合とは別に、もう一つの本番ニーズが Lance テーブルを Iceberg のカタログ上で扱うことです。これはユーザーから最も関心を集めているテーマで、Apache Hive MetaStore、Apache Polaris、Apache Gravitino、AWS Glue、Unity Catalog といった主要なカタログとの連携を意味します。
ここでの発想は、Iceberg と Lance のどちらか一方を選ばせるのではなく、同じカタログがその両方を束ねるという点にあります。データインフラの担当者にとっては、テーブルフォーマットが Iceberg であろうと Lance であろうと、入口となるカタログが一つにまとまっている方が扱いやすいからです。
この半年で、私たちは主要カタログとの連携の空白をほぼ埋めました。最も基本的な Hive MetaStore から、現在多くの企業が使う Polaris、Gravitino、Unity といった商用寄りのソリューションまで、Lance テーブルを登録して扱えるようにしています。これにより、Iceberg テーブルと Lance テーブルを同じカタログに同居させられます。
REST 標準への対応

カタログ連携で特に手応えがあるのが REST 経由の連携です。私たちが Lance 側で開発した REST API は、Iceberg REST Catalog の規格に寄せて設計しています。すでに Iceberg REST Catalog に慣れた企業であれば、Lance の取り込みが容易になります。
Iceberg REST Catalog は、カタログ操作を HTTP の REST API として標準化した仕様です。データエコシステムを抱える企業の多くは、すでに Iceberg 向けの REST API を一通り運用しています。Lance のために新たなプロトコルを覚え直すのではなく、Lance Namespace 用の REST API を既存の REST 基盤に追加する形で済むため、データインフラチームに歓迎されています。
この設計が導入の後押しになっており、すでに Iceberg を使ってきた利用者が、同じ仕組みで BI 用途と AI 用途の双方をまかなう例が出てきています。
提供する REST API は連携の深さに応じて幅を持たせています。最小限の連携で済ませたい場合は、ごく少数の API を呼ぶだけで完結します。より踏み込んだ連携を望む場合は、20 を超える API を使い分けられます。
Change Data Capture による同期

フォーマットやカタログを共有する代わりに、二つのテーブルを並立させて同期で結ぶアプローチもあります。これが Lance の行リネージ(row lineage)に基づく CDC です。
CDC(Change Data Capture)は、テーブルへの変更(追加・更新・削除)を差分イベントとして取り出す仕組みです。Lance では各行に安定した行アドレスが割り当てられるため、その行リネージをたどって変更分を導出できます。図では Ray が Lance テーブルへ書き込み、Spark が Iceberg テーブルを読む構成が示されており、カタログには lance_table と iceberg_table が並んで登録されています。Lance 側の Manifest・Fragment・Deletion File・Index File と、Iceberg 側の metadata.json・manifest-list.avro・manifest ファイルが、それぞれの構造で対応します。
この CDC から Lance 側の変更ストリームを取り出して Iceberg へ書き込む、あるいはその逆も可能で、Lance テーブルと Iceberg テーブルを互いに同期した二つの状態として保てます。フォーマット側の統合には踏み込まない企業の多くが、この二テーブル並立による同期方式を選んでいます。

この取り組みは完全にコミュニティ主導で進んでいます。半年前の初回発表のあと、想定を超える数の人々が関心を寄せてくれました。私たちは両フォーマットがそれぞれの用途で使い分けられ、Multimodal Lakehouse の構想を実現できる方向へ向けて、統合を能動的に進めています。この半年で協力してくれた企業として、Netflix、Uber、ByteDance、Alibaba、Ants Group、Luma AI、Minimax、Xiaomi、Seven Research などが名を連ねています。
まとめ
半年前には構想に留まっていた Lance と Iceberg の統合は、この期間でファイルフォーマット、テーブルフォーマット、カタログのいずれの層でも実装が動く段階に進みました。フォーマット 2.1 での圧縮率改善や ZoneMap・Bloom Filter の追加によって Fragment は Parquet に近い振る舞いを得て、Iceberg REST Catalog への準拠や行リネージに基づく CDC によって、既存の Iceberg 基盤に Lance を無理なく取り込めるようになっています。これらの多くがコミュニティ主導で進んでいる点が示すように、Iceberg と Lance のどちらかを選ばせるのではなく、同じカタログの下で BI 用途と AI 用途を両立させる方向へ向かっています。