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

milvus-logo
LFAI
  • Home
  • Blog
  • Milvusクラウド・スケーラブル・ベクター・データベースの進化

Milvusクラウド・スケーラブル・ベクター・データベースの進化

  • Engineering
December 21, 2021
Jun Gu

今回は、Milvusの新しいデータベースクラスターアーキテクチャをどのように設計したのか、その思考プロセスを紹介する。

Milvusベクトルデータベースの目的

Milvusベクター・データベースの構想が私たちの頭に浮かんだとき、私たちは、人々が組織においてAIの導入を加速させるのに役立つデータ・インフラストラクチャを構築したいと考えた。

この使命を果たすため、私たちはMilvusプロジェクトに2つの重要な目標を設定した。

使いやすさ

AI/MLは、新しい技術が次々と登場する新興分野である。ほとんどの開発者は、急成長しているAIの技術やツールにまったく精通していません。開発者はすでに、モデルの発見、トレーニング、チューニングにほとんどのエネルギーを費やしている。モデルによって生成された大量の埋め込みベクトルを扱うことに、さらなる労力を費やすことは難しい。言うまでもなく、大量のデータを操作することは、常に非常に困難なタスクです。

このように、「使いやすさ」は開発コストを大幅に削減できるため、私たちは非常に高い優先順位を与えています。

低ランニングコスト

生産現場におけるAIの主なハードルのひとつは、投資効果を正当化することだ。より低いランニングコストでAIアプリケーションを本番稼動させる機会が増えるだろう。そしてそれは、潜在的な利益のマージンを引き上げることにつながるだろう。

Milvus 2.0の設計原則

我々はMilvus 1.0でこれらの目標に向けてスタートを切った。しかし、特にスケーラビリティと可用性において十分とは言い難い。そこで、これらの点を改善するためにMilvus 2.0の開発に着手しました。この新バージョンのために私たちが定めた原則は以下の通りです:

  • 高いスケーラビリティと可用性を目指す
  • 成熟したクラウド・インフラストラクチャとプラクティスの上に構築する
  • クラウドにおけるパフォーマンスの最小限の妥協

言い換えれば、Milvusデータベースクラスタをクラウドネイティブなものにしたいということです。

データベース・クラスタの進化

ベクトル・データベースは、新しいタイプのデータ(ベクトル)を扱うため、データベースの新種である。しかし、他のデータベースと同じ課題を共有していることに変わりはなく、独自の要件もいくつかある。この記事の残りの部分では、既存のデータベース・クラスタの実装から学んだこと、そしてMilvusグループの新しいアーキテクチャをどのように設計したのか、その思考過程に焦点を当てたいと思います。

Milvusグループコンポーネントの実装詳細にご興味のある方は、Milvusのドキュメントをご参照ください。Milvus GitHubレポ、Milvusウェブサイト、Milvusブログにて継続的に技術記事を公開していきます。

理想的なデータベースクラスタ

"小さく狙って、小さく外す"

まず、理想的なデータベースクラスタが持つべき重要な機能を挙げてみましょう。

  1. 並行性と単一障害点なし:異なるグループメンバーに接続されたユーザは、同時に同じデータの読み取り/書き込みアクセスを行うことができる。
  2. 一貫性:異なるグループ・メンバーが同じデータを見ることができる。
  3. スケーラビリティ:グループメンバーの追加や削除が可能。

正直なところ、これらの能力をすべて一緒に獲得するのは難しい。最近のデータベース・クラスタの実装では、これらの機能のいくつかを妥協しなければならない。ユーザーは、ユーザーシナリオに適合する限り、完璧なデータベースクラスタを求めてはいない。しかし、共有型クラスタは、かつては理想的なデータベースクラスタに非常に近かった。何かを学びたいのであれば、ここから始めるべきである。

データベースクラスタの主な検討事項

共有クラスタには、他の最新の実装と比較して、より長い歴史がある。Db2データ共有グループとOracle RACは、共有型クラスタの典型です。多くの人は、共有化とはディスクを共有することだと考えている。それ以上のものだ。

共有エブリシング・クラスターは、グループ内に1種類のデータベース・メンバーしか持ちません。ユーザーはこれらの対称メンバーのいずれかに接続して、あらゆるデータにアクセスできる。これを機能させるために共有する必要がある「すべて」とは何だろうか?

グループ内のイベントの順序

まず、グループのイベントシーケンスは、異なるグループメンバーからの同時アクセスによって引き起こされる潜在的な競合を解決するために非常に重要です。私たちは通常、データベースのログレコードのシーケンス番号を使ってイベントシーケンスを表します。同時に、ログレコードのシーケンス番号は一般的にタイムスタンプから生成されます。

したがって、グループイベントシーケンスの要件は、グローバルタイマーの要件に等しい。もしグループ用の原子時計があれば、それは素晴らしいことである。しかし、Milvusはオープンソースのソフトウェア・プロジェクトである。現在でも、原子時計は大企業にとっては高価なオプションである。

私たちはMilvus 2.0データベースクラスタに時刻同期コンポーネントを実装しました。付録のリンクを参照してください。

グローバルロック

データベースは楽観的ロックであれ悲観的ロックであれ、同時アクセス競合を解決するためのロック機構を持っています。同様に、異なるグループメンバー間での同時アクセス競合を解決するためにグローバルロックが必要です。

グローバル・ロッキングとは、異なるグループ・メンバー同士がロック要求をネゴシエートするために会話しなければならないことを意味する。このグローバル・ロック・ネゴシエーション・プロセスの効率には、いくつかの重要な要因が影響する:

  • システム間接続の速度
  • ネゴシエーション・プロセスに参加するグループ・メンバーの数
  • グループの衝突の頻度

一般的なグループのサイズは100以下である。たとえば、Db2 DSG は 32、Oracle RAC は 100 です。これらのグループメンバーは、転送レイテンシーを最小化するために、光ファイバーで接続された1つのサーバールームに配置される。そのため、集中型クラスタと呼ばれることもある。グループ・サイズの制限のために、人々はハイエンドのサーバー(CPU、メモリー、I/Oチャンネルなどの容量がはるかに大きいメインフレームやミニコンピューター)を選択し、すべてを共有するクラスターを構成する。

このハードウェアの前提は、現代のクラウド環境では劇的に変化している。現在、クラウドデータセンターは、TCP/IP接続を備えた(何千台もの)汎用X86サーバーでいっぱいの高密度のサーバールームで構成されている。これらのX86サーバーに依存してデータベース・クラスターを構築する場合、グループ・サイズは数百台(数千台)にまで増加するはずだ。また、ビジネスシナリオによっては、この数百台のX86マシンを異なる地域に分散させたい場合もあるでしょう。そのため、グローバルロッキングを実装する価値はもうないかもしれません。

Milvus 2.0では、グローバルロック機能を実装するつもりはありません。一方では、ベクターデータの更新がない。(そのため、Milvusグループでシャーディングを行う場合、同一データに対する複数ライターの競合を心配する必要はない。この間、MVCC(ロック回避並行性制御手法の一つであるマルチバージョン並行性制御)を使って、リーダライタの競合を解決することができる。

一方、ベクトル・データ処理は、構造化データ処理よりもはるかに高いメモリ・フットプリントを消費する。ベクターデータベースには、より高いスケーラビリティが求められている。

共有メモリ内データキャッシュ

データベース・エンジンを簡単に分けると、ストレージ・エンジンとコンピューティング・エンジンの2つに分けられる。ストレージエンジンは2つの重要なタスクを担当する:

  • 耐久性のためにデータを永久記憶装置に書き込む。
  • 永続ストレージからインメモリーデータキャッシュ(別名バッファプール)にデータをロードする。

データベース・クラスタのシナリオでは、メンバーAがメンバーBにキャッシュされたデータを更新したらどうなるでしょうか?メンバーBはどうやってインメモリデータの有効期限切れを知ることができるでしょうか?古典的な共有クラスタには、この問題を解決するためのバッファ交差無効化メカニズムがあります。バッファ交差無効化メカニズムは、グループ・メンバー間で強力な一貫性を維持すれば、グローバル・ロックと同様に機能します。前述したように、現代のクラウド環境では実用的ではない。そこで我々は、Milvusクラウドスケーラブルグループの一貫性レベルを、最終的な一貫性のあり方まで下げることにした。こうすることで、Milvus 2.0のバッファクロス無効化メカニズムは非同期処理となる。

共有ストレージ

共有ストレージは、おそらくデータベースクラスタについて議論するときに最初に思いつくことでしょう。

近年のクラウドストレージの進化に伴い、ストレージの選択肢も大きく変わった。ストレージ・アタッチド・ネットワーク(SAN)は、何でも共有するグループのストレージ基盤だった(そして今もそうだ)。しかし、クラウド環境ではSANは存在しない。データベースはクラウド仮想マシンに接続されたローカルディスクを使用しなければならない。ローカル・ディスクを使用すると、グループ・メンバー間でのデータの一貫性という課題が生じる。また、グループメンバーの高可用性についても心配しなければならない。

そこでSnowflakeは、クラウド共有ストレージ(S3ストレージ)を使ったクラウドデータベースの素晴らしいロールモデルを作った。これはMilvus 2.0にもインスピレーションを与えている。前述の通り、我々は成熟したクラウドインフラに依存するつもりだ。しかし、クラウド共有ストレージを利用する前に、いくつかのことを考えなければならない。

まず、S3ストレージは安価で信頼性が高いが、データベースのシナリオのように即座にR/Wアクセスできるようには設計されていない。Milvus 2.0ではデータノードと呼んでいる)データコンポーネントを作成し、ローカルメモリ/ディスクとS3ストレージの橋渡しをする必要がある。Alluxio、JuiceFSなど)いくつかの例を学ぶことができる。これらのプロジェクトを直接統合できない理由は、データの粒度が異なるからです。AlluxioやJuiceFSはデータセットやPOSIXファイル用に設計されていますが、私たちはデータレコード(ベクトル)レベルに焦点を当てています。

ベクターデータがS3ストレージに落ち着いたら、メタデータの答えは簡単だ。では、ログデータはどうだろうか?古典的な実装では、ログストアもSANをベースにしている。あるデータベースグループメンバーのログファイルは、障害復旧のためにデータベースクラスタ内で共有される。だから、クラウド環境に入るまでは、これは問題ではなかった。

Spannerの論文の中で、GoogleはPaxosコンセンサス・アルゴリズムを使ってグローバルに分散されたデータベース(グループ)を実装する方法を説明している。データベース・クラスターをステートマシン・レプリケーション・グループとしてプログラムする必要がある。通常、REDOログはグループ全体でレプリケートされる「状態」である。

コンセンサス・アルゴリズムによるREDOログ・レプリケーションは強力なツールであり、ビジネス・シナリオによっては大きなメリットがある。しかし、Milvusベクターデータベースでは、ステートマシンのレプリケーショングループを作成する十分なインセンティブが見いだせない。我々は、ログストア用の代替クラウド共有ストレージとして、クラウド・メッセージング・キュー/プラットフォーム(Apache Pulsar、Apache Kafkaなど)を使用することにした。ログストアをメッセージング・プラットフォームに委ねることで、以下のようなメリットが得られる。

  • グループはよりイベントドリブンであるため、多くの処理を非同期で行うことができる。スケーラビリティが向上する。
  • コンポーネントがより疎結合になり、オンライン・ローリング・アップグレードがより容易になる。可用性と操作性が向上する。

このトピックについては、後のセクションで再確認する。

ここまでで、データベース・クラスタに関する重要な検討事項をまとめました。Milvus 2.0アーキテクチャの議論に入る前に、まずMilvusにおけるベクターの管理方法について説明します。

データ管理とパフォーマンス予測可能性

Milvusはベクターをコレクションに格納します。コレクション」は論理的な概念で、SQLデータベースの「テーブル」に相当します。コレクション "はベクターを保管するために複数の物理ファイルを持つことができます。物理ファイルは "セグメント "である。セグメント "はSQLデータベースのテーブルスペースファイルのような物理的な概念である。データ量が少なければ、1つのセグメント/物理ファイルにすべてを保存することができる。しかし現在、私たちは常にビッグデータに直面している。複数のセグメント/物理ファイルがある場合、どのようにデータを異なるデータ・パーティションに分散させればいいのだろうか?

データはインデックスよりも優先されますが、多くの場合、インデックス・アルゴリズムが効率的にデータにアクセスするために好む方法でデータを格納しなければなりません。SQLデータベースでよく使われる戦略は、パーティショニング・キー値の範囲によるパーティショニングです。通常、パーティショニング・キーを強制するためにクラスタ化インデックスを作成します。全体として、これはSQLデータベースにとって適切なアプローチである。データは良い形で保存され、I/O(プリフェッチ)も最適化されている。しかし、まだ欠点もある。

  • データの歪みだ。あるパーティションは他のパーティションよりはるかに多くのデータを持っているかもしれない。実世界のデータの分布は、数値範囲のように単純ではない。
  • アクセスのホットスポット。一部のデータ・パーティションにより多くの作業負荷がかかる可能性がある。

より多くのデータを持つパーティションに、より多くのワークロードが行くことを想像してください。このような状況が発生した場合、パーティション間でデータのバランスを調整する必要があります。(これがDBAの面倒な日常です)。

The Clustered index for vectors ベクトルのクラスタ化インデックス

ベクトル用のクラスタ化インデックス(転置リストインデックス)を作成することもできます。しかし、これはSQLデータベースとは異なります。SQLデータベースでは、いったんインデックスが構築されると、インデックスを通してデータにアクセスするのは非常に効率的で、計算量もI/O操作も少なくて済みます。しかし、ベクトル・データの場合、インデックスがあっても計算やI/O操作ははるかに多くなる。そのため、先に述べた不具合は、ベクターデータベースのクラスタにより深刻な影響を与えることになる。さらに、データ量と計算の複雑さのために、異なるセグメント間でベクトルをリバランスするコストは非常に高くなります。

Milvusでは、成長による分割という戦略を採用している。ベクトルコレクションにデータを注入すると、Milvusは新しいベクトルをコレクション内の最新のセグメントに追加します。Milvusはセグメントのサイズが十分に大きくなると(閾値は設定可能)、そのセグメントを閉じ、閉じたセグメントのインデックスを作成します。その間に、新しいセグメントが作成され、次のデータが格納されます。このシンプルな戦略は、ベクトル処理に適しています。

ベクトルクエリは、ベクトルコレクションから最も類似した候補を検索する処理です。典型的なMapReduceの手順である。例えば、10個のセグメントからなるベクトルコレクションから、上位20個の類似した結果を検索したいとします。各セグメントで上位 20 件を検索し、20 * 10 件の結果をマージして最終的な 20 件とします。各セグメントは同じ量のベクトルを持ち、同じようなインデックスを持つので、 各セグメントでの処理時間はほとんど変わりません。これにより、データベースクラスタの規模を計画する際に不可欠な、性能予測可能性という利点が得られる。

Milvus 2.0の新しいパラダイム

Milvus 1.0では、一般的なSQLデータベースと同様に、読み書き分割シャーディンググループを実装しました。これはMilvusデータベースクラスタをスケールさせる良い試みでした。しかし、問題も明らかでした。

Milvus database 1.0 Milvusデータベース1.0

Milvus 1.0では、R/Wノードはベクターの追加、インデックスのないセグメントでの検索、インデックスの構築など、最新のセグメントを完全に管理しなければならない。各コレクションにはライタが1つしかないため、データが連続的にシステムにストリーミングされると、ライタは非常に忙しくなる。R/Wノードとリーダーノード間のデータ共有のパフォーマンスも問題である。その上、共有データストレージはNFS(安定しない)かプレミアムクラウドストレージ(高すぎる)に頼らざるを得ない。

Milvus 1.0のアーキテクチャでは、これらの既存の問題に取り組むことは困難である。そこで、Milvus 2.0ではこれらの問題を解決するために新しいパラダイムを導入した。

Milvus architecture Milvusアーキテクチャ

アクターモデル

並行計算システムのプログラムには2つのモデルがある。

  • 同時実行制御(ロック)と同期処理を意味する共有メモリ
  • アクターモデル(別名メッセージパッシング)は、メッセージ駆動と非同期処理を意味する。

この2つのモデルは、分散データベースクラスタにも適用できる。

前述したように、知名度の高い分散データベースの多くは、コンセンサス・アルゴリズムによるREDO-LOGレプリケーションという同じ方式を採用している。これは、コンセンサス・アルゴリズムを使ってREDOログ・レコード用の分散共有メモリを構築する同期処理である。さまざまな企業やベンチャーキャピタルが、この技術に数十億ドルを投資している。Milvus 2.0に着手するまでは、私はこの技術についてコメントしたくなかった。多くの人が、この技術を分散データベースシステムを実現する唯一の方法だと考えている。これは迷惑な話だ。何も言わなければ、分散データベース設計に無謀だったと誤解されかねない。

近年、コンセンサス・アルゴリズムによるRedo-logレプリケーションは、最も過大評価されているデータベース技術である。重要な問題は2つある。

  • REDOログ・レプリケーションが優れているという思い込みがもろい。
  • ベンダーがコンセンサス・アルゴリズムの能力について人々の期待をミスリードしている。

ソースノードとターゲットノードという2つのデータベースノードがあるとしよう。ソースノードとターゲットノード、2つのデータベースノードがあるとする。ソースノードでいくつかの変更操作(I/U/D SQL文)を行い、ターゲットノードを更新し続けたいとします。どうすればいいでしょうか?最も単純な方法は、ターゲット・ノードで操作を再生することです。しかし、これは最も効率的な方法ではありません。

I/U/D文の実行コストを考えると、実行準備部分と物理的作業部分に分けることができる。実行準備部分には、SQLパーサーやSQLオプティマイザーなどの作業が含まれる。いくつのデータレコードが影響を受けるとしても、これは固定コストです。物理的作業部分のコストは、影響を受けるデータレコードの数に依存する。REDO-LOGレプリケーションの背後にある考え方は、ターゲット・ノードの固定コストを節約することであり、ターゲット・ノードではREDO-LOG(物理的作業)のみを再生する。

コスト削減率はREDO-LOGレコード数の逆数である。1つの操作が1つのレコードにしか影響しないのであれば、REDO-LOGレプリケーションによってかなりの節約ができるはずだ。それが10,000レコードだったらどうだろう?その場合、ネットワークの信頼性を心配しなければならない。1つの操作と10,000件のREDO-LOGレコードを送るのと、どちらが信頼できるだろうか?100万レコードならどうだろう?REDOログ・レプリケーションは、決済システムやメタデータ・システムなどのシナリオで威力を発揮する。これらのシナリオでは、各データベースのI/U/D操作は少数のレコード(1~2件)にしか影響しない。しかし、バッチジョブのようなI/O集約的なワークロードでは難しい。

ベンダーは常に、コンセンサス・アルゴリズムがデータベース・クラスタに強力な一貫性を提供できると主張している。しかし、コンセンサス・アルゴリズムはREDOログレコードの複製にしか使われていない。REDOログレコードは異なるノードで一貫していますが、他のノードのデータビューも一貫しているとは限りません。REDO-LOGレコードを実際のテーブルレコードにマージしなければならない。そのため、この同期処理を使用しても、データ・ビューでは最終的な一貫性しか得られません。

コンセンサス・アルゴリズムによるREDO-LOGレプリケーションを適切な場所で使うべきだ。Milvus 2.0で使われているメタデータ・システム(ETCD)とメッセージング・プラットフォーム(Apache Pulsarなど)には、コンセンサス・アルゴリズムが実装されている。しかし、前にも言ったように、"Milvusベクトル・データベースについては、全体としてステートマシンのレプリケーション・グループであるための十分なインセンティブが見いだせない"。

Milvus 2.0では、アクターモデルを使ってワーカーノードを組織している。ワーカーノードは孤独だ。メッセージング・プラットフォームと会話し、コマンドを受け取って結果を送るだけだ。退屈に聞こえる。

"我々のモットーは?" "退屈は常に最良である" - 『ヒットマンズ・ボディガード』(2017年

アクターモデルは非同期だ。スケーラビリティと可用性に適している。ワーカーノードはお互いを知らないので、ワーカーノードの一部が参加したり削除されたりしても、他のワーカーノードへの影響はない。

可用性と耐久性の分離

Milvus2.0では、ログリプレイではなく、操作リプレイを行う。ベクトルデータベースでは、操作リプレイもログリプレイも大差ないからである。Milvus2.0ではログ再生ではなく操作再生を行っています。また、アクターモデルで操作リプレイを行う方がはるかに簡単です。

そのため、複数のワーカーノードがそれぞれの責任に応じて、メッセージングプラットフォームから同じ操作を実行する可能性があります。Milvusデータベースクラスタの共有ストレージレイヤーとしてS3クラウドストレージを使用することに決めたことは前述した。S3ストレージは非常に信頼性が高い。では、異なるワーカーノードが同じデータを共有ストレージに書き出す必要があるのでしょうか?

そこで、ワーカーノードには3つの役割を持たせました。

  • クエリノードは、割り当てに従ってインメモリデータビューを維持する。クエリノードの仕事には、ベクトル検索とインメモリデータの更新が含まれる。しかし、S3ストレージに何かを書き込む必要はない。グループの中で最もメモリを必要とするノードである。
  • データノードは新しいデータをS3ストレージに書き込む役割を担う。データノードはインメモリデータビューを維持する必要がないため、データノードのハードウェア構成はクエリノードとは全く異なる。
  • インデックスノードは、セグメントのサイズが閾値に達すると、データノードによってクローズされたセグメントに対してインデックスを構築する。これはグループの中で最もCPU負荷の高い作業である。

これら3種類のノードは、それぞれ異なる種類の作業負荷を表している。これらは独立してスケールすることができる。私たちはこれを、マイクロソフトのクラウドデータベースSocratesから学んだ可用性と耐久性の分離と呼んでいる。

終わりであり、始まりでもある

この記事では、milvusベクトルデータベース2.0のいくつかの設計上の決定についてレビューしてきた。 ここでそれらの点を手短にまとめよう。

  • 我々はMilvusクラスタ2.0の最終的な一貫性を選択した。
  • 成熟したクラウドコンポーネントをMilvus 2.0にできる限り統合した。Milvus 2.0によって導入された新しいコンポーネントをユーザーの本番環境にコントロールしました。
  • アクターモデルを採用し、可用性と耐久性を分離することで、Milvus 2.0はクラウド環境での拡張が容易になっています。

ここまでで、Milvus 2.0のクラウドスケーラブルなデータベースのバックボーンが出来上がりましたが、バックログにはMilvusコミュニティからの満たすべき多くの要求が含まれています。もしあなたが同じミッション(「AIトランスフォーメーションを加速するために、より多くのオープンソース・インフラストラクチャ・ソフトウェアを構築する」)を持っているなら、Milvusコミュニティへの参加を歓迎します。

MilvusはLF AI & Data Foundationの卒業プロジェクトです。MilvusのためにCLAに署名する必要はありません!

付録

Milvusデザインドキュメント

https://github.com/milvus-io/milvus/tree/master/docs/design_docs

C++によるRaftの実装

もしまだコンセンサスアルゴリズムに興味があるなら、eBayのオープンソースプロジェクトGringoftsをチェックすることをお勧めする。これはRaftコンセンサス・アルゴリズム(Paxosファミリーの一種)のC++実装だ。私の友人のJackyとElvis(モルガン・スタンレーでの私の元同僚)は、eBayのオンライン決済システムのためにこれを構築した。

Try Managed Milvus for Free

Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

Get Started

Like the article? Spread the word

続けて読む