Construir uma arquitetura multilocatário para o Atlas Vector Search
Nesta página
Você pode implementar a multilocação com o Atlas Vector Search para que uma única instância de um aplicativo atenda a vários locatários. Esta página descreve recomendações de design que se aplicam especificamente ao Atlas Vector Search. Essas recomendações diferem das nossas recomendações de multilocação para o Atlas.
Recomendações
Consulte as seguintes recomendações ao projetar uma arquitetura multilocatário para o Atlas Vector Search.
Importante
Esta orientação pressupõe que você pode colocar locatários em uma única VPC. Caso contrário, você deve manter projetos separados para cada locatário, o que não recomendamos no Atlas Vector Search.
Uma coleção para todos os locatários
Recomendamos armazenar todos os dados do locatário em uma única coleção, assim como em um único banco de dados e cluster. Você pode distinguir entre locatários incluindo um campo tenant_id
em cada documento. Este campo pode ser qualquer identificador único para o locatário, como um UUID ou um nome de locatário. Você pode usar este campo como um pré-filtro em seus índices e consultas do Atlas Vector Search.
Essa abordagem centralizada oferece os seguintes benefícios:
Fácil de modelar e dimensionar.
Simplifica as operações de manutenção.
Roteamento eficiente de query por meio de pré-filtragem por
tenant_id
.Observação
Você tem a garantia de não servir locatários que não correspondam a este filtro.
Uma coleção por locatário
Não recomendamos armazenar cada locatário em uma coleção separada, pois essa abordagem pode levar a cargas variáveis de fluxo de alterações dependendo do número de coleções. Isso pode ter um impacto negativo no desempenho e nas funcionalidades de monitoramento. As garantias de isolamento de dados no Atlas se aplicam ao nível do banco de dados, portanto, não há benefício adicional de isolamento de dados ao usar várias coleções.
Em vez disso, use uma coleção para todos os locatários. Para um exemplo de como migrar de um modelo de coleção por locatário para um modelo de coleção única, veja Migrando de um modelo de coleção por locatário.
Considerações
Considere as seguintes estratégias para mitigar possíveis problemas de desempenho com a abordagem recomendada.
Discrepâncias de tamanho do locatário
Se você enfrentar problemas de desempenho devido à distribuição desigual de dados (alguns locatários grandes e muitos locatários pequenos), utilize o MongoDB Views para separar locatários grandes de locatários menores:
Locatários grandes (1% principal):
Crie uma visualização para cada grande locatário.
Crie um índice para cada visualização.
Mantenha um registro de grandes locatários que você verifica no momento da query para direcionar as queries de forma adequada.
Pequenos Locatários (Locatários Remanescentes):
Crie uma única visualização para todos os pequenos locatários.
Construa um único índice para esta visualização.
Utilize o campo
tenant_id
como um pré-filtro para direcionar queries de forma adequada.
Exemplo
O exemplo a seguir demonstra como criar visualizações para grandes e pequenos locatários usando mongosh
:
Mantenha um registro de seus grandes locatários e seus valores tenant_id
correspondentes, e então crie uma visualização para cada um desses locatários:
db.createView( "<viewName>", "<collectionName>", [ { "$match": { "tenant_id": "<largeTenantId>" } } ] )
Crie uma visualização para os pequenos locatários, com filtro para os grandes locatários:
db.createView( "<viewName>", "<collectionName>", [ { "$match": { "tenant_id": { "$nin": [ "<largeTenantId1>", "<largeTenantId2>", ... ] } } } ] )
Após criar as visualizações, crie os índices para cada visualização. Verifique o seguinte:
Ao especificar o nome da coleção para o índice, utilize o nome da visualização em vez do nome original da coleção.
Certifique-se de que o índice na visualização do pequeno locatário inclua o campo
tenant_id
como um pré-filtro.
Consulte a página Criar índices para obter instruções sobre como criar índices.
Muitos locatários grandes
Se você tiver muitos locatários, cada um com um grande número de vetores, considere usar um sistema baseado em partições distribuindo dados entre fragmentos.
Você pode usar o campo tenant_id
como uma chave de fragmento para distribuir os dados em faixas específicas com base no ID do locatário. Para obter mais informações, consulte Fragmentação à distância.
Migrando de um modelo de coleção por locatário
Para migrar de um modelo de coleção por locatário para um modelo de coleção única, processe cada coleção de locatário e insira os documentos em uma nova coleção.
Por exemplo, o script a seguir usa o driver Node.js para migrar seus dados de um modelo de coleção por locatário para um modelo de coleção única. O script também inclui um campo tenant_id
para cada documento com base no nome da coleção de origem.
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);