Compartir a través de


Migre su aplicación de Amazon DynamoDB a Azure Cosmos DB

SE APLICA A: NoSQL

Azure Cosmos DB es una base de datos totalmente administrada, escalable y distribuida globalmente. Proporciona acceso de baja latencia garantizado a los datos.

En este artículo se describe cómo migrar la aplicación .NET de Amazon DynamoDB a Azure Cosmos DB con cambios mínimos en el código. Para obtener más información sobre Azure Cosmos DB, vea el artículo de información general.

Diferencias conceptuales

En la tabla siguiente se enumeran las principales diferencias conceptuales entre Azure Cosmos DB y DynamoDB:

DynamoDB Azure Cosmos DB
No aplicable Base de datos
Tabla Colección
Elemento Documento
Atributo Campo
Índice secundario Índice secundario
Clave principal > clave de partición Clave de partición
Clave primaria > clave de ordenación No es necesario
STREAM Fuente de cambios
Unidad de proceso de escritura Unidad de solicitud (flexible, se puede usar para lecturas o escrituras)
Unidad de proceso de lectura Unidad de solicitud (flexible, se puede usar para lecturas o escrituras)
Tabla global No es necesario. Puede seleccionar directamente la región al aprovisionar la cuenta de Azure Cosmos DB. (Puede cambiar la región más adelante).

Diferencias estructurales

La estructura JSON de Azure Cosmos DB es más sencilla que la estructura JSON de DynamoDB. En el ejemplo siguiente se muestran las diferencias.

DynamoDB

El siguiente objeto JSON representa el formato de datos en 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

El siguiente objeto JSON representa el formato de datos en Azure Cosmos DB:

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

Migración del código

Este artículo tiene como ámbito migrar el código de una aplicación a Azure Cosmos DB, que es un aspecto crítico de la migración de bases de datos. Para ayudarle a comprender cómo funciona el proceso de migración, en las secciones siguientes se compara el código entre Amazon DynamoDB y Azure Cosmos DB.

Para descargar el código fuente, clone el siguiente repositorio:

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

Prerrequisitos

  • .NET Framework 4.7.2.
  • Versión más reciente de Visual Studio con la carga de trabajo de desarrollo de Azure. Puede empezar con el IDE gratuito de Visual Studio Community. Habilite la carga de trabajo de desarrollo de Azure durante la configuración de Visual Studio.
  • Acceso a una cuenta de Azure Cosmos DB para NoSQL.
  • Instalación local de Amazon DynamoDB.
  • Java 8.
  • Versión descargable de Amazon DynamoDB. Ejecútelo en el puerto 8000. (Puede cambiar y configurar el código).

Configuración del código

Agregue el siguiente paquete NuGet al proyecto:

Install-Package Microsoft.Azure.Cosmos

Establecer una conexión

DynamoDB

En Amazon DynamoDB, se usa el código siguiente para conectarse:

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

Azure Cosmos DB

Para conectarse a Azure Cosmos DB, actualice el código para:

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

Optimización de la conexión en Azure Cosmos DB

Con Azure Cosmos DB, puede usar las siguientes opciones para optimizar la conexión:

  • ConnectionMode: use el modo de conexión directa para conectarse a los nodos de datos del servicio Azure Cosmos DB. Use el modo de puerta de enlace solo para inicializar y almacenar en caché las direcciones lógicas y actualizar en las actualizaciones. Para más información, consulte Modos de conectividad del SDK de SQL de Azure Cosmos DB.

  • ApplicationRegion: use esta opción para establecer la región con replicación geográfica preferida para interactuar con Azure Cosmos DB. Para más información, consulte Distribución de datos globalmente con Azure Cosmos DB.

  • ConsistencyLevel: use esta opción para invalidar el nivel de coherencia predeterminado. Para más información, consulte Niveles de coherencia en Azure Cosmos DB.

  • BulkExecutionMode: Use esta opción para ejecutar operaciones masivas estableciendo la propiedad AllowBulkExecution a true. Para más información, consulte Importación masiva de datos en una cuenta de Azure Cosmos DB para NoSQL mediante el SDK de .NET.

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

Cree el contenedor.

DynamoDB

Para almacenar los datos en Amazon DynamoDB, primero debe crear la tabla. Defina el esquema, el tipo de clave y los atributos, como se muestra en el código siguiente:

// 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

En Amazon DynamoDB, debe aprovisionar las unidades de proceso de lectura y las unidades de proceso de escritura. En Azure Cosmos DB, especifique el rendimiento como unidades de solicitud por segundo (RU/s). Puede utilizar RU/s para cualquier operación de manera dinámica. Los datos se organizan como base de datos, contenedor y, después, elemento. Puede especificar el rendimiento en el nivel de base de datos, en el nivel de colección o en ambos.

Para crear una base de datos:

await client_cosmosDB.CreateDatabaseIfNotExistsAsync(movies_table_name);

Para crear un contenedor:

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

Carga de los datos

DynamoDB

En el código siguiente se muestra cómo cargar los datos en Amazon DynamoDB. El código moviesArray enumera los documentos JSON y luego debe iterarlos y cargarlos en 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

En Azure Cosmos DB, puede optar por transmitir y escribir mediante moviesContainer.CreateItemStreamAsync(). Sin embargo, en este ejemplo, el JSON se deserializa en el tipo MovieModel para demostrar la capacidad de conversión de tipos. El código es multiproceso y usa la arquitectura distribuida en Azure Cosmos DB para acelerar la carga.

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);

Creación de un documento

DynamoDB

Escribir un nuevo documento en Amazon DynamoDB no garantiza la seguridad de tipo. En el ejemplo siguiente se usa newItem como tipo de documento:

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

Azure Cosmos DB

Azure Cosmos DB proporciona seguridad de tipos a través de un modelo de datos. En este ejemplo se usa un modelo de datos denominado 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);
    }
}

En Azure Cosmos DB, newItem es 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;

Lectura de un documento

DynamoDB

Para leer en Amazon DynamoDB, debe definir primitivas:

// 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

Con Azure Cosmos DB, la consulta es 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;
    }

La colección de documentos del ejemplo anterior es segura para tipos y proporciona una opción de consulta natural.

Actualización de un elemento

DynamoDB

Para actualizar un elemento en Amazon DynamoDB:

updateResponse = await client.UpdateItemAsync( updateRequest );

Azure Cosmos DB

En Azure Cosmos DB, una actualización se trata como una Upsert operación (es decir, se inserta el documento si no existe):

await moviesContainer.UpsertItemAsync<MovieModel>(updatedMovieModel);

Eliminar un documento

DynamoDB

Para eliminar un elemento en Amazon DynamoDB, debe volver a recurrir a los 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

En Azure Cosmos DB, puede obtener el documento y eliminarlo de forma asincrónica:

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));
  }
  }

Consulta de documentos

DynamoDB

En Amazon DynamoDB, se requieren funciones de API para consultar los datos:

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

En Azure Cosmos DB, puede realizar la proyección y el filtro dentro de una consulta SQL simple:

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 las operaciones de rango (por ejemplo, between), debe realizar un examen en 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( );

En Azure Cosmos DB, puede usar una consulta SQL y una instrucción de una sola línea:

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");

Eliminación de un contenedor

DynamoDB

Para eliminar la tabla en Amazon DynamoDB, puede especificar:

client.DeleteTableAsync( tableName );

Azure Cosmos DB

Para eliminar la colección en Azure Cosmos DB, puede especificar:

await moviesContainer.DeleteContainerAsync();

A continuación, elimine también la base de datos, si es necesario:

await cosmosDatabase.DeleteAsync();

Resumen

Como se muestra en los ejemplos anteriores, Azure Cosmos DB admite consultas naturales (SQL) y las operaciones son asincrónicas. Puede migrar fácilmente el código complejo a Azure Cosmos DB. El código se vuelve más sencillo después de la migración.