🚀 Zilliz Cloudを無料で試す、完全管理型のMilvus—10倍の高速パフォーマンスを体験しよう!今すぐ試す>>

milvus-logo
LFAI
  • Home
  • Blog
  • ベクターデータベースでデータはどのように処理されるのか?

ベクターデータベースでデータはどのように処理されるのか?

  • Engineering
March 28, 2022
Zhenshan Cao

Cover image 表紙画像

この記事はZhenshan Caoが執筆し、Angela Niがトランスコードしたものです。

このブログシリーズの前の2つの投稿では、世界で最も先進的なベクトル・データベースであるMilvusのシステム・アーキテクチャと、そのPython SDKとAPIについてすでに説明した。

本記事では、Milvusのシステムに深く入り込み、データ処理コンポーネント間の相互作用を検証することで、Milvusでデータがどのように処理されるかを理解していただくことを主な目的としています。

始める前に役立つリソースを以下に示します。本記事のトピックをより深く理解するために、まずこれらをお読みになることをお勧めします。

MsgStreamインターフェース

MsgStreamインターフェイスはmilvusのデータ処理において非常に重要である。Start() が呼び出されると、バックグラウンドのコルーチンがデータをログブローカに書き込んだり、ログブローカからデータを読み込んだりします。Close() が呼び出されると、コルーチンは停止します。

MsgStream interface MsgStream インターフェース

MsgStreamはプロデューサとしてもコンシューマとしても機能する。AsProducer(channels []string) インターフェースはMsgStreamをプロデューサーとして定義し、AsConsumer(channels []string, subNamestring)はコンシューマーとして定義している。パラメータchannels は両インターフェースで共有され、どの(物理)チャネルにデータを書き込むか、またはどのチャネルからデータを読み取るかを定義するために使用される。

コレクション内のシャードの数は、コレクションの作成時に指定できる。各シャードは仮想チャネル(vchannel)に対応する。したがって、コレクションは複数のvchannelを持つことができます。Milvusはログブローカ内の各vchannelに物理チャネル(pchannel)を割り当てます。

Each virtual channel/shard corresponds to a physical channel. 各仮想チャネル/シャードは物理チャネルに対応します

Produce() MsgStream インターフェースは、ログブローカの pchannel にデータを書き込む役割を果たします。データは2つの方法で書き込むことができる:

  • 単一書き込み: エンティティは主キーのハッシュ値によって異なるシャード(vchannel)に書き込まれる。その後、これらのエンティティはログブローカの対応するpchannelsに流れ込む。
  • ブロードキャスト書き込み: エンティティは、パラメータchannels で指定されたすべての pchannels に書き込まれる。

Consume() はブロッキングAPIの一種である。指定されたpchannelに利用可能なデータがない場合、MsgStreamインターフェースで が呼び出されると、コルーチンはブロックされる。一方、 はノンブロッキングAPIであり、指定されたpchannelに既存のデータがある場合にのみ、コルーチンはデータを読み込んで処理する。そうでなければ、コルーチンは他のタスクを処理することができ、利用可能なデータがないときにブロックされることはない。Consume() Chan()

Seek() は障害回復のためのメソッドです。新しいノードが起動すると、 を呼び出すことで、データ消費記録を取得し、中断したところからデータ消費を再開することができます。Seek()

データの書き込み

異なるVチャネル(シャード)に書き込まれるデータには、挿入メッセージと削除メッセージがある。これらのvchannelsはDmChannels(データ操作チャネル)とも呼ばれます。

異なるコレクションはログブローカ内で同じ pchannels を共有することができます。1つのコレクションは複数のシャードを持つことができ、したがって複数の対応するvchannelsを持つことができます。同じコレクション内のエンティティは、結果的にログブローカ内の複数の対応するpchannelsに流れます。その結果、pchannelsを共有する利点は、ログブローカの高い同時実行性によって可能になるスループットの増大です。

コレクションが作成されるとき、シャードの数が指定されるだけでなく、ログブローカ内のvchannelsとpchannels間のマッピングも決定されます。

Write path in Milvus Milvusにおける書き込みパス

上の図に示すように、書き込み経路では、プロキシがMsgStreamのAsProducer() インタフェースを介してログブローカにデータを書き込む。その後、データノードがデータを消費し、消費されたデータを変換してオブジェクトストレージに格納します。ストレージ・パスは、データ・コーディネータがetcdに記録するメタ情報の一種である。

フローグラフ

異なるコレクションがログブローカ内で同じpchannelを共有する可能性があるため、データを消費する際、データノードやクエリノードはpchannel内のデータがどのコレクションに属するかを判断する必要がある。この問題を解決するために、Milvusではflowgraphを導入しました。これは主にコレクションIDによって共有pchannel内のデータをフィルタリングする役割を担っている。つまり、各flowgraphは、コレクション内の対応するシャード(vchannel)のデータストリームを処理する。

Flowgraph in write path 書き込みパスのフローグラフ

MsgStreamの生成

データを書き込む際、以下の2つのシナリオでMsgStreamオブジェクトが生成される:

  • プロキシがデータ挿入要求を受信すると、まずルート・コーディネータ(root coordinator)を介してvchannelsとpchannelsの間のマッピングを取得しようとする。次に、プロキシはMsgStreamオブジェクトを作成する。

Scenario 1 シナリオ1

  • データノードが起動し、etcd内のチャネルのメタ情報を読み込むと、MsgStreamオブジェクトが生成される。

Scenario 2 シナリオ2

データの読み込み

Read path in Milvus Milvusにおける読み込みパス

データ読み込みの一般的なワークフローを上の図に示す。クエリ要求はDqRequestChannelを介してクエリノードにブロードキャストされる。クエリノードはクエリタスクを並列実行する。クエリーノードからのクエリー結果はgRPCを経由し、プロキシが結果を集約してクライアントに返す。

データ読み取りプロセスを詳しく見ると、プロキシがDqRequestChannelにクエリ要求を書き込んでいることがわかる。そして、クエリノードはDqRequestChannelをサブスクライブすることでメッセージを消費する。DqRequestChannelの各メッセージはブロードキャストされ、サブスクライブしたすべてのクエリノードがメッセージを受信できる。

クエリノードはクエリ要求を受信すると、密封されたセグメントに格納されたバッチデータと、Milvusに動的に挿入され、成長するセグメントに格納されたストリーミングデータの両方に対してローカルクエリを実行する。その後、クエリノードは密閉セグメントと成長セグメントの両方でクエリ結果を集約する必要がある。これらの集約結果はgRPCを介してプロキシに渡される。

プロキシは複数のクエリノードからすべての結果を収集し、それらを集約して最終的な結果を得る。そして、プロキシは最終的なクエリ結果をクライアントに返す。各クエリリクエストとそれに対応するクエリ結果は同じ一意なrequestIDでラベル付けされているので、プロキシはどのクエリリリクエストにどのクエリ結果が対応するかを把握することができる。

フローグラフ

Flowgraph in read path 読み取りパスのフローグラフ

書き込みパスと同様に、フローグラフは読み取りパスにも導入される。Milvusは、インクリメンタルデータとヒストリカルデータの処理を統合するユニファイドラムダアーキテクチャを実装している。そのため、クエリノードはリアルタイムのストリーミングデータも取得する必要がある。同様に、読み取りパスのフローグラフは、異なるコレクションからのデータをフィルタリングし、区別する。

MsgStreamの作成

Creating MsgStream object in read path 読み取りパスでのMsgStreamオブジェクトの作成

データを読み込む際、以下のシナリオでMsgStreamオブジェクトを作成します:

  • Milvusでは、データはロードされないと読み込むことができません。プロキシはデータロード要求を受け取ると、その要求をクエリコーディネータに送信し、クエリコーディネータは異なるクエリノードへのシャードの割り当て方法を決定します。割り当て情報(vchannelの名前、vchannelと対応するpchannel間のマッピング)は、メソッドコールまたはRPC(リモートプロシージャコール)を介してクエリノードに送信される。その後、クエリ・ノードは対応するMsgStreamオブジェクトを作成してデータを消費します。

DDL 操作

DDL はデータ定義言語の略です。メタデータに対する DDL 操作は、書き込み要求と読み取り要求に分類できます。ただし、メタデータ処理中、この2種類の要求は同等に扱われます。

メタデータの読み取り要求には以下が含まれます:

  • クエリ・コレクション・スキーマ
  • クエリ・インデキシング情報。

書き込みリクエストには以下が含まれます:

  • コレクションの作成
  • コレクションの削除
  • インデックスの作成
  • インデックスの削除 など

DDL リクエストはクライアントからプロキシに送られ、プロキシは受け取った順番に これらのリクエストをルートコーデックに渡し、ルートコーデックは各 DDL リクエストにタイムスタンプを割り当て、リクエストの動的チェックを行います。プロキシは各リクエストを一度に一つのDDLリクエストを意味するシリ アルな方法で操作する。プロキシは、前のリクエストの処理を完了し、ルートコー ドから結果を受け取るまで、次のリクエストを処理しない。

DDL operations. DDL操作

上の図に示すように、ルート・コー ディネート・タスク・キューにはK DDL要求がある。タスク・キュー内の DDL 要求は、ルート・コー ディネートが受信した順に並べられる。つまり、ddl1 がルート・コー ディネートに最初に送られたもので、ddlK がこのバッチの最後のものである。ルートコーデ ィックは時間順に一つずつリクエストを処理する。

分散システムでは、プロキシとルートコーデック間の通信はgRPCによって 有効にされる。ルートコーデ ィションは、すべてのDDLリクエストが時間順に処理されることを保証するた めに、実行されたタスクの最大タイムスタンプ値の記録を保持する。

2つの独立したプロキシ、プロキシ1とプロキシ2があるとする。両者は同じルートコー ドにDDLリクエストを送る。しかし、一つの問題は、先に送られたリクエストが、後で別のプロキシが 受け取ったリクエストよりも先にルートコーダに送られるとは限らない ことである。例えば、上の画像では、DDL_K-1 がプロキシ1からルートコー ディネートに送られたとき、プロキシ2からのDDL_K はすでにルートコー ディネートによって受け入れられ、実行されている。ルートコー ドによって記録されるように、この時点で実行されたタスクの最大タ イムスタンプ値は、K である。したがって、時間順 序を中断しないために、リクエストDDL_K-1 はルートコー ドのタスクキューによって拒否される。しかし、プロキシ2がこの時点でルートコー ドにリクエストDDL_K+5 を送ると、リクエストはタスクキューに受 け入れられ、そのタイムスタンプ値に従って後で実行される。

インデックスの構築

インデックスの構築

クライアントからインデックス構築リクエストを受け取ると、プロキシはまずリクエストの 静的チェックを行い、それをルートコーデックに送る。それからルートコーディネータはこれらのインデックス構築要求をメタストレージ(etcd)に永続化し、インデックスコーディネータ(index coordinator)に要求を送る。

Building an index. インデックスの構築

上図のように、インデックス・コーディネータはルート・コーディネータからインデックス構築要求を受け取ると、まずメタ・ストレージのetcdにタスクを永続化する。インデックス構築タスクの初期ステータスはUnissued である。インデックス・コーダは、各インデックス・ノードのタスク負荷の記録を保持し、負荷の少ないインデックス・ノードに受信タスクを送ります。タスクが完了すると、インデックスノードはそのタスクのステータス、Finished またはFailed をmilvusのetcdであるメタストレージに書き込みます。その後、インデックス・ノードはetcdを検索することで、インデックス構築タスクが成功したか失敗したかを理解します。タスクがシステムリソースの制限やインデックスノードの脱落により失敗した場合、インデックスコーデックはプロセス全体を再トリガーし、同じタスクを別のインデックスノードに割り当てます。

インデックスの削除

さらに、インデックス・コーディネータはインデックスの削除依頼も担当します。

Dropping an index. インデックスの削除

ルート・コーダはクライアントからインデックスの削除要求を受け取ると、まずそのインデックスを "dropped "としてマークし、その結果をインデックス・コーダに通知しながらクライアントに返します。次に、インデックス・コーダはすべてのインデックス作成タスクをIndexID でフィルタリングし、条件に一致するタスクをドロップする。

インデックス・コーディネーターのバックグラウンド・コルーチンは、オブジェクト・ストレージ(MinIOとS3)から、"droped "とマークされたすべてのインデックス作成タスクを徐々に削除する。このプロセスには、recycleIndexFilesインターフェースが関係します。対応するインデックスファイルがすべて削除されると、削除されたインデックス作成タスクのメタ情報がメタストレージ(etcd)から削除されます。

ディープ・ダイブ・シリーズについて

Milvus 2.0の一般提供の正式発表に伴い、Milvusのアーキテクチャとソースコードの詳細な解釈を提供するために、このMilvus Deep Diveブログシリーズを企画しました。このブログシリーズで扱うトピックは以下の通りです:

Like the article? Spread the word

続けて読む