Partilhar via


Migrar seu aplicativo do Amazon DynamoDB para o Azure Cosmos DB

APLICA-SE A: NoSQL

O Azure Cosmos DB é um banco de dados escalável, distribuído globalmente e totalmente gerenciado. Ele fornece acesso garantido de baixa latência aos seus dados.

Este artigo descreve como migrar seu aplicativo .NET do Amazon DynamoDB para o Azure Cosmos DB com alterações mínimas de código. Para saber mais sobre o Azure Cosmos DB, consulte o artigo de visão geral .

Diferenças conceptuais

A tabela a seguir lista as principais diferenças conceituais entre o Azure Cosmos DB e o DynamoDB:

DynamoDB Azure Cosmos DB (base de dados altamente escalável e distribuída globalmente da Microsoft)
Não aplicável Base de Dados
Tabela Coleção
Item Documento
Atributo Campo
Índice secundário Índice secundário
Chave primária > chave de partição Chave de partição
Chave primária > chave de classificação Não obrigatório
Fluxo Feed de alterações
Escrever unidade de computação Unidade de solicitação (flexível, pode ser usada para leituras ou gravações)
Ler unidade de processamento Unidade de solicitação (flexível, pode ser usada para leituras ou gravações)
Tabela global Não é necessário. Você pode selecionar diretamente a região ao provisionar a conta do Azure Cosmos DB. (Você pode alterar a região mais tarde.)

Diferenças estruturais

A estrutura JSON do Azure Cosmos DB é mais simples do que a estrutura JSON do DynamoDB. O exemplo a seguir mostra as diferenças.

DynamoDB

O seguinte objeto JSON representa o formato de dados no DynamoDB:

{
TableName: "Music",
KeySchema: [
{ 
  AttributeName: "Artist",
  KeyType: "HASH", //Partition key
},
{ 
  AttributeName: "SongTitle",
  KeyType: "RANGE" //Sort key
}
],
AttributeDefinitions: [
{ 
  AttributeName: "Artist",
  AttributeType: "S"
},
{ 
  AttributeName: "SongTitle",
  AttributeType: "S"
}
],
ProvisionedThroughput: {
  ReadCapacityUnits: 1,
  WriteCapacityUnits: 1
 }
}

Azure Cosmos DB

O seguinte objeto JSON representa o formato de dados no Azure Cosmos DB:

{
"Artist": "",
"SongTitle": "",
"AlbumTitle": "",
"Year": 9999,
"Price": 0.0,
"Genre": "",
"Tags": ""
}

Migrar o seu código

Este artigo tem como escopo migrar o código de um aplicativo para o Azure Cosmos DB, que é um aspeto crítico da migração de banco de dados. Para ajudá-lo a entender como funciona o processo de migração, as seções a seguir comparam o código entre o Amazon DynamoDB e o Azure Cosmos DB.

Para baixar o código-fonte, clone o seguinte repositório:

git clone https://github.com/Azure-Samples/DynamoDB-to-CosmosDB

Pré-requisitos

  • .NET Framework 4.7.2.
  • Versão mais recente do Visual Studio com a carga de trabalho de desenvolvimento do Azure. Você pode começar a usar o IDE gratuitoda Comunidade do Visual Studio. Habilite a carga de trabalho de desenvolvimento do Azure durante a instalação do Visual Studio.
  • Acesso a uma conta do Azure Cosmos DB para NoSQL.
  • Instalação local do Amazon DynamoDB.
  • Java 8.
  • Versão para download do Amazon DynamoDB. Execute-o na porta 8000. (Você pode alterar e configurar o código.)

Configure o seu código

Adicione o seguinte pacote NuGet ao seu projeto:

Install-Package Microsoft.Azure.Cosmos

Estabeleça uma conexão

DynamoDB

No Amazon DynamoDB, você usa o seguinte código para se conectar:

    AmazonDynamoDBConfig addbConfig = new AmazonDynamoDBConfig() ;
        addbConfig.ServiceURL = "endpoint";
        try { aws_dynamodbclient = new AmazonDynamoDBClient( addbConfig ); }

Azure Cosmos DB

Para conectar o Azure Cosmos DB, atualize seu código para:

client_documentDB = new CosmosClient(
    "<nosql-account-endpoint>",
    tokenCredential
);

Otimizar a conexão no Azure Cosmos DB

Com o Azure Cosmos DB, você pode usar as seguintes opções para otimizar sua conexão:

  • ConnectionMode: Use o modo de conexão direta para se conectar aos nós de dados no serviço Azure Cosmos DB. Use o modo de gateway apenas para inicializar e armazenar em cache os endereços lógicos e atualizar durante as modificações. Para obter mais informações, consulte Modos de conectividade do Azure Cosmos DB SQL SDK.

  • ApplicationRegion: Use esta opção para definir a região replicada geograficamente preferida para interagir com o Azure Cosmos DB. Para obter mais informações, consulte Distribuir seus dados globalmente com o Azure Cosmos DB.

  • ConsistencyLevel: Use esta opção para substituir o nível de consistência padrão. Para obter mais informações, consulte Níveis de consistência no Azure Cosmos DB.

  • BulkExecutionMode: Use esta opção para executar operações em massa definindo a AllowBulkExecution propriedade como true. Para obter mais informações, consulte Importação em massa de dados para uma conta do Azure Cosmos DB para NoSQL usando o SDK do .NET.

    client_cosmosDB = new CosmosClient(" Your connection string ",new CosmosClientOptions()
    { 
      ConnectionMode=ConnectionMode.Direct,
      ApplicationRegion=Regions.EastUS2,
      ConsistencyLevel=ConsistencyLevel.Session,
      AllowBulkExecution=true  
    });
    

Criar o contentor

DynamoDB

Para armazenar os dados no Amazon DynamoDB, você precisa criar a tabela primeiro. Defina o esquema, o tipo de chave e os atributos, conforme mostrado no código a seguir:

// movies_key_schema
public static List<KeySchemaElement> movies_key_schema
  = new List<KeySchemaElement>
{
  new KeySchemaElement
  {
    AttributeName = partition_key_name,
    KeyType = "HASH"
  },
  new KeySchemaElement
  {
    AttributeName = sort_key_name,
    KeyType = "RANGE"
  }
};

// key names for the Movies table
public const string partition_key_name = "year";
public const string sort_key_name      = "title";
  public const int readUnits=1, writeUnits=1; 

    // movie_items_attributes
    public static List<AttributeDefinition> movie_items_attributes
  = new List<AttributeDefinition>
{
  new AttributeDefinition
  {
    AttributeName = partition_key_name,
    AttributeType = "N"
  },
  new AttributeDefinition
  {
    AttributeName = sort_key_name,
    AttributeType = "S"
  }

CreateTableRequest  request;
CreateTableResponse response;

// Build the 'CreateTableRequest' structure for the new table
request = new CreateTableRequest
{
  TableName             = table_name,
  AttributeDefinitions  = table_attributes,
  KeySchema             = table_key_schema,
  // Provisioned-throughput settings are always required,
  // although the local test version of DynamoDB ignores them.
  ProvisionedThroughput = new ProvisionedThroughput( readUnits, writeUnits );
};

Azure Cosmos DB

No Amazon DynamoDB, você precisa provisionar as unidades de computação de leitura e as unidades de computação de gravação. No Azure Cosmos DB, você especifica a taxa de transferência como unidades de solicitação por segundo (RU/s). Você pode usar RU/s para qualquer operação dinamicamente. Os dados são organizados como banco de dados, contêiner e, em seguida, item. Você pode especificar a taxa de transferência no nível do banco de dados, no nível da coleção ou em ambos.

Para criar um banco de dados:

await client_cosmosDB.CreateDatabaseIfNotExistsAsync(movies_table_name);

Para criar um contêiner:

await cosmosDatabase.CreateContainerIfNotExistsAsync(new ContainerProperties() { PartitionKeyPath = "/" + partitionKey, Id = new_collection_name }, provisionedThroughput);

Carregar os dados

DynamoDB

O código a seguir mostra como carregar os dados no Amazon DynamoDB. O moviesArray código lista documentos JSON e, em seguida, você precisa iterar e carregar os documentos JSON no Amazon DynamoDB.

int n = moviesArray.Count;
for( int i = 0, j = 99; i < n; i++ )
    {
  try
  {
    string itemJson = moviesArray[i].ToString();
    Document doc = Document.FromJson(itemJson);
    Task putItem = moviesTable.PutItemAsync(doc);
    if( i >= j )
    {
      j++;
      Console.Write( "{0,5:#,##0}, ", j );
      if( j % 1000 == 0 )
        Console.Write( "\n " );
      j += 99;
    }
    await putItem;

Azure Cosmos DB

No Azure Cosmos DB, você pode optar por transmitir e gravar usando moviesContainer.CreateItemStreamAsync(). No entanto, neste exemplo, o JSON é desserializado para o tipo MovieModel para demonstrar a funcionalidade de conversão de tipo. O código é de multithreading e utiliza a arquitetura distribuída no Azure Cosmos DB para acelerar a velocidade de carregamento.

List<Task> concurrentTasks = new List<Task>();
for (int i = 0, j = 99; i < n; i++)
{
  try
  {
      MovieModel doc= JsonConvert.DeserializeObject<MovieModel>(moviesArray[i].ToString());
      doc.Id = Guid.NewGuid().ToString();
      concurrentTasks.Add(moviesContainer.CreateItemAsync(doc,new PartitionKey(doc.Year)));
      {
          j++;
          Console.Write("{0,5:#,##0}, ", j);
          if (j % 1000 == 0)
              Console.Write("\n               ");
          j += 99;
      }
      
  }
  catch (Exception ex)
  {
      Console.WriteLine("\n     ERROR: Could not write the movie record #{0:#,##0}, because:\n       {1}",
                          i, ex.Message);
      operationFailed = true;
      break;
  }
}
await Task.WhenAll(concurrentTasks);

Criar um documento

DynamoDB

Escrever um novo documento no Amazon DynamoDB não é seguro. O exemplo a seguir usa newItem como o tipo de documento:

Task<Document> writeNew = moviesTable.PutItemAsync(newItem, token);
await writeNew;

Azure Cosmos DB

O Azure Cosmos DB fornece segurança de tipo por meio de um modelo de dados. Este exemplo usa um modelo de dados chamado MovieModel:

public class MovieModel
{
    [JsonProperty("id")]
    public string Id { get; set; }
    [JsonProperty("title")]
    public string Title{ get; set; }
    [JsonProperty("year")]
    public int Year { get; set; }
    public MovieModel(string title, int year)
    {
        this.Title = title;
        this.Year = year;
    }
    public MovieModel()
    {

    }
    [JsonProperty("info")]
    public   MovieInfo MovieInfo { get; set; }

    internal string PrintInfo()
    {
        if(this.MovieInfo!=null)
        return            string.Format("\nMovie with title:{1}\n Year: {2}, Actors: {3}\n Directors:{4}\n Rating:{5}\n", this.Id, this.Title, this.Year, String.Join(",",this.MovieInfo.Actors), this.MovieInfo, this.MovieInfo.Rating);
        else
            return string.Format("\nMovie with  title:{0}\n Year: {1}\n",  this.Title, this.Year);
    }
}

No Azure Cosmos DB, newItem é MovieModel:

 MovieModel movieModel = new MovieModel()
            {
                Id = Guid.NewGuid().ToString(),
                Title = "The Big New Movie",
                Year = 2018,
                MovieInfo = new MovieInfo() { Plot = "Nothing happens at all.", Rating = 0 }
            };
    var writeNew= moviesContainer.CreateItemAsync(movieModel, new Microsoft.Azure.Cosmos.PartitionKey(movieModel.Year));
    await writeNew;

Ler um documento

DynamoDB

Para ler no Amazon DynamoDB, você precisa definir primitivos:

// Create primitives for the HASH and RANGE portions of the primary key
Primitive hash = new Primitive(year.ToString(), true);
Primitive range = new Primitive(title, false);

  Task<Document> readMovie = moviesTable.GetItemAsync(hash, range, token);
  movie_record = await readMovie;

Azure Cosmos DB

Com o Azure Cosmos DB, a consulta é natural (LINQ):

IQueryable<MovieModel> movieQuery = moviesContainer.GetItemLinqQueryable<MovieModel>(true)
                        .Where(f => f.Year == year && f.Title == title);
// The query is executed synchronously here, but can also be executed asynchronously via the IDocumentQuery<T> interface
    foreach (MovieModel movie in movieQuery)
    {
      movie_record_cosmosdb = movie;
    }

A coleção de documentos no exemplo anterior é tipo seguro e fornece uma opção de consulta natural.

Atualizar um item

DynamoDB

Para atualizar um item no Amazon DynamoDB:

updateResponse = await client.UpdateItemAsync( updateRequest );

Azure Cosmos DB

No Azure Cosmos DB, uma atualização é considerada uma operação de Upsert (ou seja, insere o documento se ele não existir):

await moviesContainer.UpsertItemAsync<MovieModel>(updatedMovieModel);

Eliminar um documento

DynamoDB

Para excluir um item no Amazon DynamoDB, você novamente precisa usar primitivos:

Primitive hash = new Primitive(year.ToString(), true);
      Primitive range = new Primitive(title, false);
      DeleteItemOperationConfig deleteConfig = new DeleteItemOperationConfig( );
      deleteConfig.ConditionalExpression = condition;
      deleteConfig.ReturnValues = ReturnValues.AllOldAttributes;
      
  Task<Document> delItem = table.DeleteItemAsync( hash, range, deleteConfig );
        deletedItem = await delItem;

Azure Cosmos DB

No Azure Cosmos DB, você pode obter o documento e excluí-lo de forma assíncrona:

var result= ReadingMovieItem_async_List_CosmosDB("select * from c where c.info.rating>7 AND c.year=2018 AND c.title='The Big New Movie'");
while (result.HasMoreResults)
{
  var resultModel = await result.ReadNextAsync();
  foreach (var movie in resultModel.ToList<MovieModel>())
  {
    await moviesContainer.DeleteItemAsync<MovieModel>(movie.Id, new PartitionKey(movie.Year));
  }
  }

Consultar documentos

DynamoDB

No Amazon DynamoDB, as funções da API são necessárias para consultar os dados:

QueryOperationConfig config = new QueryOperationConfig( );
  config.Filter = new QueryFilter( );
  config.Filter.AddCondition( "year", QueryOperator.Equal, new DynamoDBEntry[ ] { 1992 } );
  config.Filter.AddCondition( "title", QueryOperator.Between, new DynamoDBEntry[ ] { "B", "Hzz" } );
  config.AttributesToGet = new List<string> { "year", "title", "info" };
  config.Select = SelectValues.SpecificAttributes;
  search = moviesTable.Query( config ); 

Azure Cosmos DB

No Azure Cosmos DB, você pode fazer projeção e filtro dentro de uma consulta SQL simples:

var result = moviesContainer.GetItemQueryIterator<MovieModel>( 
  "select c.Year, c.Title, c.info from c where Year=1998 AND (CONTAINS(Title,'B') OR CONTAINS(Title,'Hzz'))");

Para operações de intervalo (por exemplo, between), você precisa fazer uma verificação no Amazon DynamoDB:

ScanRequest sRequest = new ScanRequest
{
  TableName = "Movies",
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#yr", "year" }
  },
  ExpressionAttributeValues = new Dictionary<string, AttributeValue>
  {
      { ":y_a", new AttributeValue { N = "1960" } },
      { ":y_z", new AttributeValue { N = "1969" } },
  },
  FilterExpression = "#yr between :y_a and :y_z",
  ProjectionExpression = "#yr, title, info.actors[0], info.directors, info.running_time_secs"
};

ClientScanning_async( sRequest ).Wait( );

No Azure Cosmos DB, você pode usar uma consulta SQL e uma instrução de linha única:

var result = moviesContainer.GetItemQueryIterator<MovieModel>( 
  "select c.title, c.info.actors[0], c.info.directors,c.info.running_time_secs from c where BETWEEN year 1960 AND 1969");

Eliminar um contentor

DynamoDB

Para excluir a tabela no Amazon DynamoDB, você pode especificar:

client.DeleteTableAsync( tableName );

Azure Cosmos DB

Para excluir a coleção no Azure Cosmos DB, você pode especificar:

await moviesContainer.DeleteContainerAsync();

Em seguida, exclua o banco de dados também, se necessário:

await cosmosDatabase.DeleteAsync();

Resumo

Como mostram os exemplos anteriores, o Azure Cosmos DB dá suporte a consultas naturais (SQL) e as operações são assíncronas. Você pode migrar facilmente seu código complexo para o Azure Cosmos DB. O código torna-se mais simples após a migração.