準備
この記事では、主にMilvusのメモリにベクターデータがどのように記録され、その記録がどのように維持されるかについて説明する。
主な設計目標は以下の通りである:
- データのインポート効率が高いこと。
- データインポートの効率が良いこと。
- データファイルの断片化を避ける。
そのため、データを挿入するためのメモリ・バッファ(挿入バッファ)を設け、ディスクやオペレーティング・システムでのランダムIOのコンテキスト・スイッチの回数を減らし、データ挿入のパフォーマンスを向上させました。MemTableとMemTableFileをベースとしたメモリストレージアーキテクチャにより、より便利にデータを管理しシリアライズすることができる。バッファの状態はMutableとImmutableに分かれており、外部サービスを利用可能な状態に保ちながら、データをディスクに永続化することができる。
準備
Milvusにベクトルを挿入する準備ができたら、まずCollectionを作成する必要がある(※Milvusは0.7.0バージョンでTableをCollectionに改名している)。コレクションはMilvusでベクトルを記録・検索するための最も基本的な単位です。
各コレクションには固有の名前と設定可能なプロパティがあり、コレクション名に基づいてベクターの挿入や検索が行われます。新しいコレクションを作成すると、Milvusはそのコレクションの情報をメタデータに記録します。
データ挿入
ユーザがデータを挿入するリクエストを送信すると、データはシリアライズされ、デシリアライズされてMilvusサーバに到達します。データはメモリに書き込まれます。メモリへの書き込みは以下のステップに大別される:
2-データ挿入-milvus.png
- MemManagerで、コレクションの名前に対応する新しいMemTableを見つけるか作成する。各 MemTable は、メモリ内の Collection バッファに対応します。
- MemTable には、1 つ以上の MemTableFile が含まれます。新しい MemTableFile を作成するたびに、この情報を同時に Meta に記録します。MemTableFileを2つの状態に分ける:MutableとImmutableである。MemTableFileのサイズが閾値に達すると、Immutableになる。各MemTableは、常に1つのMutable MemTableFileしか書き込むことができない。
- 各MemTableFileのデータは、最終的にセット・インデックス・タイプのフォーマットでメモリに記録される。MemTableFileは、メモリ上のデータを管理するための最も基本的な単位である。
- いつでも、挿入されたデータのメモリ使用量が、あらかじめ設定された値(insert_buffer_size)を超えることはない。これは、データの挿入要求が来るたびに、MemManagerが各MemTableに含まれるMemTableFileが占有するメモリを簡単に計算し、現在のメモリに応じて挿入要求を調整できるからである。
MemManager、MemTable、MemTableFileのマルチレベルアーキテクチャにより、データ挿入をより適切に管理・維持することができる。もちろん、それ以上のこともできる。
ほぼリアルタイムのクエリー
Milvusでは、挿入されたデータがメモリからディスクに移動するまで、長くても1秒待つだけでよい。このプロセス全体を大まかにまとめると次のようになる:
2-near-real-time-query-milvus.png
まず、挿入されたデータはメモリ内の挿入バッファに入る。バッファは、シリアライゼーションの準備のために、定期的に最初のMutable状態からImmutable状態へと変化する。そして、これらのImmutableバッファは、バックグラウンドのシリアライズスレッドによって定期的にディスクにシリアライズされる。データが配置された後、順序情報がメタデータに記録される。この時点で、データを検索することができる!
では、図のステップを詳しく説明しよう。
データをミュータブル・バッファーに挿入するプロセスはすでに知っている。次のステップは、ミュータブル・バッファからイミュータブル・バッファへの切り替えです:
3-mutable-buffer-immutable-buffer-milvus.png
イミュータブル・キューは、バックグラウンドのシリアライゼーション・スレッドに、イミュータブルな状態とシリアライゼーションの準備が整ったMemTableFileを提供します。各MemTableは、それ自身のイミュータブル・キューを管理し、MemTableの唯一の変更可能なMemTableFileのサイズが閾値に達すると、イミュータブル・キューに入ります。ToImmutableを担当するバックグラウンド・スレッドは、MemTableが管理するイミュータブル・キュー内のすべてのMemTableFileを定期的に取り出し、それらをトータルのイミュータブル・キューに送信する。注意しなければならないのは、データをメモリに書き込む操作と、メモリ内のデータを書き込めない状態に変更する操作の2つは、同時に発生することはなく、共通のロックが必要であることだ。しかし、ToImmutableの操作は非常に単純で、ほとんど遅延が発生しないため、挿入されたデータに対するパフォーマンスへの影響は最小限である。
次のステップは、シリアライズキュー内のMemTableFileをディスクにシリアライズすることである。これは主に3つのステップに分かれる:
4-シリアライズ-Memtablefile-milvus.png
まず、バックグラウンドのシリアライズ・スレッドが、不変キューからMemTableFileを定期的に取り出します。その後、固定サイズのRawファイル(Raw TableFiles)にシリアライズされる。最後に、この情報をメタデータに記録する。ベクトル検索を行う際には、メタデータ内の対応するTableFileにクエリーをかける。ここから、これらのデータを検索することができる!
また、設定されたindex_file_sizeに従って、直列化スレッドが直列化サイクルを完了した後、いくつかの固定サイズのTableFileをTableFileにマージし、これらの情報もメタデータに記録する。この時点で、TableFileにインデックスを付けることができる。インデックス構築も非同期である。インデックス構築を担当する別のバックグラウンド・スレッドは、メタデータのToIndex状態のTableFileを定期的に読み込み、対応するインデックス構築を実行する。
ベクトル検索
実際、TableFileとメタデータの助けを借りれば、ベクトル検索がより直感的で便利になることがわかるだろう。一般的には、メタデータからクエリ対象のCollectionに対応するTableFileを取得し、それぞれのTableFileを検索し、最後にマージする必要がある。今回は、検索の具体的な実装については触れない。
もっと詳しくお知りになりたい方は、ソースコードをお読みいただくか、Milvusに関する他の技術記事をお読みください!
- データ挿入
- ほぼリアルタイムのクエリー
- ベクトル検索
On This Page
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word