Atlas Vector Search のマルチテナント アーキテクチャの構築
項目一覧
Atlas Vector Search でマルチテナンシーを実装すると、アプリケーションの 1 つのインスタンスが複数のテナントにサービスを提供できます。このページでは、Atlas Vector Search に特に適用される設計上の推奨事項について説明します。これらの推奨事項は、Atlas のマルチテナンシーに関する推奨事項とは異なります。
推奨事項
Atlas Vector Search のマルチテナント アーキテクチャを設計する際には、次の推奨事項を参照してください。
重要
このガイダンスでは、単一の VPC 内にテナントを配置できると仮定しています。そうでない場合は、テナントごとに別々のプロジェクトを管理する必要があるため、Atlas Vector Search はお勧めしません。
すべてのテナントに対して 1 つのコレクション
すべてのテナントデータを単一のコレクション、データベース、およびクラスターに保存することをお勧めします。各ドキュメント内に tenant_id
フィールドを含めることで、テナントを区別できます。このフィールドは、UUID やテナント名など、テナントの一意の識別子にすることができます。このフィールドは、Atlas Vector Search のインデックスやクエリのプレフィルターとして使用できます。
この集中型アプローチには、以下の利点があります。
モデル化とスケーリングが容易
メンテナンス操作を簡素化
tenant_id
によるプレフィルターでクエリルーティングを効率化注意
このフィルターに一致しないテナントにはサービスを提供しないことが保証されます。
テナントごとに 1 つのコレクション
各テナントを個別のコレクションに保存することはお勧めしません。この方法では、コレクションの数に応じて変更ストリームの負荷が変化する可能性があるためです。これにより、パフォーマンスとモニタリング機能に悪影響を与える可能性があります。Atlas のデータ分離保証はデータベースレベルで適用されるため、複数のコレクションを使用しても追加のデータ分離の利点はありません。
代わりに、すべてのテナントに対して1つのコレクションを使用してください。テナントごとのコレクション モデルから単一コレクション モデルへの移行方法の例については、「テナントごとのコレクション モデルからの移行」をご覧ください。
Considerations
次の戦略を考慮し、推奨アプローチを使用してパフォーマンスに関する潜在的な問題を軽減してください。
テナントのサイズ不一致
データの分散が不均一であるため(一部の大規模テナントと多数の小規模テナント)、パフォーマンスの問題が発生した場合は、MongoDBビューを使用して大規模テナントと小規模テナントを分離します。
大規模テナント(上位 1%):
大規模なテナントごとにビューを作成してください。
各ビューに対してインデックスを作成します。
大規模テナントの記録を保持し、クエリ時に確認してそれに応じてクエリをルーティングします。
小規模テナント(残りのテナント):
すべての小規模テナント用にビューを 1 つ作成します。
このビューのインデックスを 1 つ作成します。
tenant_id
フィールドをプレフィルターとして使用し、それに応じてクエリをルーティングします。
例
次の例では、mongosh
を使用して大規模テナントと小規模テナントのビューを作成する方法を示します。
大規模テナントとそれに対応する tenant_id
の値を記録し、これらのテナントごとにビューを作成します。
db.createView( "<viewName>", "<collectionName>", [ { "$match": { "tenant_id": "<largeTenantId>" } } ] )
大規模テナントを除外して、小規模テナント用のビューを作成します。
db.createView( "<viewName>", "<collectionName>", [ { "$match": { "tenant_id": { "$nin": [ "<largeTenantId1>", "<largeTenantId2>", ... ] } } } ] )
ビューを作成した後、各ビューのインデックスを作成してください。次の点を確認してください。
インデックスのコレクション名を指定する際は、元のコレクション名ではなく、ビュー名を使用してください。
スモールテナントビューのインデックスに、
tenant_id
フィールドをプレフィルターとして含めることを確認してください。
インデックスの作成について詳しくは、「インデックスの作成」ページを参照してください。
多数の大規模テナント
多数のテナントがあり、それぞれに多数のベクトルがある場合は、シャード全体にデータを分散するパーティションベースのシステムの使用を検討してください。
tenant_id
フィールドをシャードキーとして使用すると、テナント ID に基づいて特定の範囲にデータを分散させることができます。詳しくは、「範囲シャーディング」を参照してください。
テナントごとのコレクション モデルからの移行
テナントごとのコレクション モデルから単一コレクション モデルに移行するには、各テナントのコレクションを処理し、ドキュメントを新しいコレクションに挿入します。
たとえば、次のスクリプトではNode.js ドライバーを使用して、テナントごとのコレクション モデルから単一コレクション モデルにデータを移行します。スクリプトには、ソース コレクションの名前に基づいて、各ドキュメントの tenant_id
フィールドも含まれています。
import { MongoClient } from 'mongodb'; const uri = "<connectionString>"; const sourceDbName = "<sourceDatabaseName>"; const targetDbName = "<targetDatabaseName>"; const targetCollectionName = "<targetCollectionName>"; async function migrateCollections() { const client = new MongoClient(uri); try { await client.connect(); const sourceDb = client.db(sourceDbName); const targetDb = client.db(targetDbName); const targetCollection = targetDb.collection(targetCollectionName); const collections = await sourceDb.listCollections().toArray(); console.log(`Found ${collections.length} collections.`); const BATCH_SIZE = 1000; // Define a suitable batch size based on your requirements let totalProcessed = 0; for (const collectionInfo of collections) { const collection = sourceDb.collection(collectionInfo.name); let documentsProcessed = 0; let batch = []; const tenantId = collectionInfo.name; // Uses the collection name as the tenant_id const cursor = collection.find({}); for await (const doc of cursor) { doc.tenant_id = tenantId; // Adds a tenant_id field to each document batch.push(doc); if (batch.length >= BATCH_SIZE) { await targetCollection.insertMany(batch); totalProcessed += batch.length; documentsProcessed += batch.length; console.log(`Processed ${documentsProcessed} documents from ${collectionInfo.name}. Total processed: ${totalProcessed}`); batch = []; } } if (batch.length > 0) { await targetCollection.insertMany(batch); totalProcessed += batch.length; documentsProcessed += batch.length; console.log(`Processed ${documentsProcessed} documents from ${collectionInfo.name}. Total processed: ${totalProcessed}`); } } console.log(`Migration completed. Total documents processed: ${totalProcessed}`); } catch (err) { console.error('An error occurred:', err); } finally { await client.close(); } } await migrateCollections().catch(console.error);