Compartilhar via


Migrar do CouchBase para o Azure Cosmos DB para NoSQL

O Azure Cosmos DB é um banco de dados escalonável, distribuído globalmente e totalmente gerenciado. Ele fornece acesso garantido de baixa latência aos seus dados. Para saber mais sobre o Azure Cosmos DB, veja o artigo de visão geral. Este artigo fornece instruções para migrar aplicativos Java conectados ao Couchbase para uma conta da API para NoSQL no Azure Cosmos DB.

Diferenças na nomenclatura

Veja a seguir os principais recursos que funcionam de forma diferente no Azure Cosmos DB quando comparados ao Couchbase:

Couchbase Azure Cosmos DB
Servidor Couchbase Conta
Bucket Base de dados
Bucket Contêiner/coleção
Documento JSON Item/Documento

Principais diferenças

  • O Azure Cosmos DB tem um campo "ID" dentro do documento, enquanto o Couchbase tem a ID como parte do bucket. O campo "ID" é exclusivo na partição.

  • O Azure Cosmos DB é dimensionado usando a técnica de particionamento ou fragmentação. O que significa que ele divide os dados em vários fragmentos/partições. Essas partições/fragmentos são criados com base na propriedade de chave de partição que você fornece. Você pode selecionar a chave de partição para otimizar as operações de leitura e gravação ou também a leitura/gravação otimizada. Para saber mais, confira o artigo de particionamento .

  • No Azure Cosmos DB, não é necessário que a hierarquia de nível superior denote a coleção porque o nome da coleção já existe. Esse recurso torna a estrutura JSON mais simples. Veja a seguir um exemplo que mostra as diferenças no modelo de dados entre o Couchbase e o Azure Cosmos DB:

    Couchbase: ID do documento = "99FF4444"

    {
      "TravelDocument":
      {
        "Country":"India",
        "Validity" : "2022-09-01",
        "Person":
        {
          "Name": "Manish",
          "Address": "AB Road, City-z"
        },
        "Visas":
        [
          {
          "Country":"India",
          "Type":"Multi-Entry",
          "Validity":"2022-09-01"
          },
          {
          "Country":"US",
          "Type":"Single-Entry",
          "Validity":"2022-08-01"
          }
        ]
      }
    }
    

    Azure Cosmos DB: consulte o “ID” dentro do documento, conforme mostrado abaixo

    {
      "id" : "99FF4444",
    
      "Country":"India",
       "Validity" : "2022-09-01",
        "Person":
        {
          "Name": "Manish",
          "Address": "AB Road, City-z"
        },
        "Visas":
        [
          {
          "Country":"India",
          "Type":"Multi-Entry",
          "Validity":"2022-09-01"
          },
          {
          "Country":"US",
          "Type":"Single-Entry",
          "Validity":"2022-08-01"
          }
        ]
      }
    

Suporte ao SDK do Java

O Azure Cosmos DB tem os seguintes SDKs (kits de desenvolvimento de software) para dar suporte a diferentes estruturas Java:

  • SDK assíncrono
  • Spring Boot SDK

As seções a seguir descrevem quando usar cada um desses SDKs. Considere um exemplo em que temos três tipos de cargas de trabalho:

Couchbase como repositório de documentos e consultas personalizadas baseadas em dados do Spring

Se a carga de trabalho que você está migrando for baseada no SDK baseado no Spring Boot, você poderá usar as seguintes etapas:

  1. Adicione o pai ao arquivo POM.xml:

    <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.1.5.RELEASE</version>
       <relativePath/>
    </parent>
    
  2. Adicione propriedades ao arquivo POM.xml:

    <azure.version>2.1.6</azure.version>
    
  3. Adicione dependências ao arquivo POM.xml:

    <dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure-cosmosdb-spring-boot-starter</artifactId>
        <version>2.1.6</version>
    </dependency>
    
  4. Adicione propriedades de aplicativo em recursos e especifique o seguinte. Substitua os parâmetros url, chave e nome do banco de dados:

       azure.cosmosdb.uri=<your-cosmosDB-URL>
       azure.cosmosdb.key=<your-cosmosDB-key>
       azure.cosmosdb.database=<your-cosmosDB-dbName>
    
  5. Defina o nome da coleção no modelo. Você também pode especificar outras anotações. Por exemplo, ID, chave de partição para denotá-los explicitamente:

    @Document(collection = "mycollection")
        public class User {
            @id
            private String id;
            private String firstName;
            @PartitionKey
            private String lastName;
        }
    

Veja a seguir os snippets de código para operações CRUD:

Operações de inserção e atualização

Onde _repo é o objeto do repositório e doc é o objeto da classe POJO. Você pode usar .save para inserir ou atualizar (se o documento com o ID especificado for encontrado). O snippet de código a seguir mostra como inserir ou atualizar um objeto doc:

_repo.save(doc);

Operação de exclusão

Considere o seguinte trecho de código, em que o objeto doc terá o ID e a chave de partição obrigatórios para localizar e excluir o objeto:

_repo.delete(doc);

Operação de leitura

Você pode ler o documento com ou sem especificar a chave de partição. Se você não especificar a chave de partição, ela será tratada como uma consulta entre partições. Considere os exemplos de código a seguir, o primeiro executará a operação usando a ID e o campo de chave de partição. O segundo exemplo usa um campo regular e sem especificar o campo de chave de partição.

  • _repo.findByIdAndName(objDoc.getId(),objDoc.getName());
  • _repo.findAllByStatus(objDoc.getStatus());

É isso, agora você pode usar seu aplicativo com o Azure Cosmos DB. O exemplo de código completo para o exemplo descrito neste documento está disponível no repositório GitHub CouchbaseToCosmosDB-SpringCosmos .

Couchbase como um repositório de documentos e usando consultas N1QL

Consultas N1QL são a maneira de definir consultas no Couchbase.

Consulta N1QL Consulta do Azure Cosmos DB
SELECT META(TravelDocument).id AS id, TravelDocument.* FROM TravelDocument WHERE _type = "com.xx.xx.xx.xxx.xxx.xxxx " and country = 'India’ and ANY m in Visas SATISFIES m.type == 'Multi-Entry' and m.Country IN ['India', Bhutan’] ORDER BY Validity DESC LIMIT 25 OFFSET 0 SELECT c.id,c FROM c JOIN m in c.country=’India’ WHERE c._type = " com.xx.xx.xx.xxx.xxx.xxxx" and c.country = 'India' and m.type = 'Multi-Entry' and m.Country IN ('India', 'Bhutan') ORDER BY c.Validity DESC OFFSET 0 LIMIT 25

Você pode observar as seguintes alterações em suas consultas N1QL:

  • Você não precisa usar a palavra-chave META ou fazer referência ao documento de primeiro nível. Em vez disso, você pode criar sua própria referência ao contêiner. Neste exemplo, consideramos isso como "c" (pode ser qualquer coisa). Essa referência é usada como um prefixo para todos os campos de primeiro nível. Por exemplo, c.id, c.country, etc.

  • Em vez de “ANY”, agora você pode fazer uma junção no subdocumento e consultá-lo com um alias dedicado, como “m”. Depois de criar o alias para um subdocumento, você precisará usar o alias. Por exemplo, m.Country.

  • A sequência de OFFSET é diferente na consulta do Azure Cosmos DB, primeiro você precisa especificar OFFSET e LIMIT. É recomendável não usar o SDK do Spring Data se você estiver usando o máximo de consultas personalizadas definidas, pois ele pode ter uma sobrecarga desnecessária no lado do cliente ao passar a consulta para o Azure Cosmos DB. Em vez disso, temos um SDK de Java Assíncrono direto, que pode ser utilizado com muita eficiência nesse caso.

Operação de leitura

Use o SDK do Java Assíncrono com as seguintes etapas:

  1. Configure a seguinte dependência no arquivo POM.xml:

    <!-- https://mvnrepository.com/artifact/com.microsoft.azure/azure-cosmosdb -->
    <dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure-cosmos</artifactId>
        <version>3.0.0</version>
    </dependency>
    
  2. Crie um objeto de conexão para o Azure Cosmos DB usando o ConnectionBuilder método, conforme mostrado no exemplo a seguir. Certifique-se de colocar essa declaração no bean de modo que o código a seguir deve ser executado apenas uma vez:

    ConnectionPolicy cp=new ConnectionPolicy();
    cp.connectionMode(ConnectionMode.DIRECT);
    
    if(client==null)
       client= CosmosClient.builder()
          .endpoint(Host)//(Host, PrimaryKey, dbName, collName).Builder()
           .connectionPolicy(cp)
           .key(PrimaryKey)
           .consistencyLevel(ConsistencyLevel.EVENTUAL)
           .build();
    
    container = client.getDatabase(_dbName).getContainer(_collName);
    
  3. Para executar a consulta, você precisa executar o seguinte snippet de código:

    Flux<FeedResponse<CosmosItemProperties>> objFlux= container.queryItems(query, fo);
    

Agora, com a ajuda do método acima, você pode passar várias consultas e executar sem nenhum incômodo. Caso você tenha o requisito de executar uma consulta grande, que pode ser dividida em várias consultas, tente o seguinte snippet de código em vez da anterior:

for(SqlQuerySpec query:queries)
{
   objFlux= container.queryItems(query, fo);
   objFlux .publishOn(Schedulers.elastic())
         .subscribe(feedResponse->
            {
               if(feedResponse.results().size()>0)
               {
                  _docs.addAll(feedResponse.results());
               }
            
            },
            Throwable::printStackTrace,latch::countDown);
   lstFlux.add(objFlux);
}
                  
      Flux.merge(lstFlux);
      latch.await();
}

Com o código anterior, você pode executar consultas em paralelo e aumentar as execuções distribuídas para otimizar. Além disso, você também pode executar as operações de inserção e atualização:

Operação de inserção

Para inserir o documento, execute o seguinte código:

Mono<CosmosItemResponse> objMono= container.createItem(doc,ro);

Em seguida, assine o Mono como:

CountDownLatch latch=new CountDownLatch(1);
objMono .subscribeOn(Schedulers.elastic())
        .subscribe(resourceResponse->
        {
           if(resourceResponse.statusCode()!=successStatus)
              {
                 throw new RuntimeException(resourceResponse.toString());
              }
           },
        Throwable::printStackTrace,latch::countDown);
latch.await();

Operação upsert

A operação upsert exige que você especifique o documento que precisa ser atualizado. Para buscar o documento completo, você pode usar o snippet mencionado na operação de leitura de título e modificar os campos necessários. O seguinte snippet de código upserts o documento:

Mono<CosmosItemResponse> obs= container.upsertItem(doc, ro);

Em seguida, assine o mono. Consulte o snippet de assinatura do mono na operação de inserção.

Operação de exclusão

O snippet a seguir executará a operação de exclusão:

CosmosItem objItem= container.getItem(doc.Id, doc.Tenant);
Mono<CosmosItemResponse> objMono = objItem.delete(ro);

Em seguida, assine mono, consulte o snippet de assinatura mono na operação de inserção. O exemplo de código completo está disponível no repositório GitHub CouchbaseToCosmosDB-AsyncInSpring .

Couchbase como um par chave/valor

Esse é um tipo simples de carga de trabalho na qual você pode executar pesquisas em vez de consultas. Use as seguintes etapas para pares chave/valor:

  1. Considere ter "/ID" como chave primária, o que garantirá que você possa executar a operação de pesquisa diretamente na partição específica. Crie uma coleção e especifique "/ID" como chave de partição.

  2. Desligue completamente a indexação. Como você executará operações de pesquisa, não faz sentido carregar a sobrecarga de indexação. Para desativar a indexação, entre no portal do Azure e acesse a conta do Azure Cosmos DB. Abra o Data Explorer, selecione o Banco de Dados e o Contêiner. Abra a guia Escala > Configurações e selecione a Política de Indexação. Atualmente, a política de indexação tem a seguinte aparência:

    {
     "indexingMode": "consistent",
     "automatic": true,
     "includedPaths": [
         {
             "path": "/*"
         }
     ],
     "excludedPaths": [
         {
             "path": "/\"_etag\"/?"
         }
     ]
     }
    

    Substitua a política de indexação acima pela seguinte política:

    {
     "indexingMode": "none",
     "automatic": false,
     "includedPaths": [],
     "excludedPaths": []
     }
    
  3. Use o snippet de código a seguir para criar o objeto de conexão. Objeto de conexão (a ser colocado em @Bean ou torná-lo estático):

    ConnectionPolicy cp=new ConnectionPolicy();
    cp.connectionMode(ConnectionMode.DIRECT);
    
    if(client==null)
       client= CosmosClient.builder()
          .endpoint(Host)//(Host, PrimaryKey, dbName, collName).Builder()
           .connectionPolicy(cp)
           .key(PrimaryKey)
           .consistencyLevel(ConsistencyLevel.EVENTUAL)
           .build();
    
    container = client.getDatabase(_dbName).getContainer(_collName);
    

Agora você pode executar as operações CRUD da seguinte maneira:

Operação de leitura

Para ler o item, use o seguinte snippet:

CosmosItemRequestOptions ro=new CosmosItemRequestOptions();
ro.partitionKey(new PartitionKey(documentId));
CountDownLatch latch=new CountDownLatch(1);
      
var objCosmosItem= container.getItem(documentId, documentId);
Mono<CosmosItemResponse> objMono = objCosmosItem.read(ro);
objMono .subscribeOn(Schedulers.elastic())
        .subscribe(resourceResponse->
        {
           if(resourceResponse.item()!=null)
           {
              doc= resourceResponse.properties().toObject(UserModel.class);
           }
        },
        Throwable::printStackTrace,latch::countDown);
latch.await();

Operação de inserção

Para inserir um item, você pode executar o seguinte código:

Mono<CosmosItemResponse> objMono= container.createItem(doc,ro);

Em seguida, assine o serviço Mono como:

CountDownLatch latch=new CountDownLatch(1);
objMono.subscribeOn(Schedulers.elastic())
      .subscribe(resourceResponse->
      {
         if(resourceResponse.statusCode()!=successStatus)
            {
               throw new RuntimeException(resourceResponse.toString());
            }
         },
      Throwable::printStackTrace,latch::countDown);
latch.await();

Operação upsert

Para atualizar o valor de um item, consulte o snippet de código abaixo:

Mono<CosmosItemResponse> obs= container.upsertItem(doc, ro);

Em seguida, assine mono, consulte o snippet de assinatura mono na operação de inserção.

Operação de exclusão

Use o snippet a seguir para executar a operação de exclusão:

CosmosItem objItem= container.getItem(id, id);
Mono<CosmosItemResponse> objMono = objItem.delete(ro);

Em seguida, assine mono, consulte o snippet de assinatura mono na operação de inserção. O exemplo de código completo está disponível no repositório GitHub CouchbaseToCosmosDB-AsyncKeyValue .

Migração de dados

Use Azure Data Factory para migrar dados. Esse é o método mais recomendado para migrar os dados. Configure a origem como Couchbase e o coletor como Azure Cosmos DB for NoSQL. Consulte o artigo Conector do Data Factory do Azure Cosmos DB do Azure para obter as etapas detalhadas.

Próximas etapas