Udostępnij za pośrednictwem


Migrowanie aplikacji z bazy danych Amazon DynamoDB do usługi Azure Cosmos DB

DOTYCZY: NoSQL

Azure Cosmos DB to skalowalna, globalnie rozproszona, w pełni zarządzana baza danych. Zapewnia gwarantowany dostęp do danych z małym opóźnieniem. Aby dowiedzieć się więcej na temat usługi Azure Cosmos DB, zobacz artykuł z omówieniem. W tym artykule opisano sposób migrowania aplikacji .NET z bazy danych DynamoDB do usługi Azure Cosmos DB z minimalnymi zmianami kodu.

Różnice koncepcyjne

Poniżej przedstawiono kluczowe różnice koncepcyjne między usługą Azure Cosmos DB i bazą danych DynamoDB:

DynamoDB Azure Cosmos DB
Nie dotyczy baza danych
Table Kolekcja
Towar Dokument
Atrybut Pole
Indeks pomocniczy Indeks pomocniczy
Klucz podstawowy — klucz partycji Klucz partycji
Klucz podstawowy — klucz sortowania Niewymagane
Stream Zmieńfeed
Pisanie jednostki obliczeniowej Jednostka żądania (elastyczna, może służyć do odczytu lub zapisu)
Odczyt jednostki obliczeniowej Jednostka żądania (elastyczna, może służyć do odczytu lub zapisu)
Tabele globalne Niewymagane. Możesz bezpośrednio wybrać region podczas aprowizowania konta usługi Azure Cosmos DB (możesz później zmienić region)

Różnice strukturalne

Usługa Azure Cosmos DB ma prostszą strukturę JSON w porównaniu z bazą danych DynamoDB. W poniższym przykładzie przedstawiono różnice

DynamoDB:

Poniższy obiekt JSON reprezentuje format danych w bazie danych 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:

Poniższy obiekt JSON reprezentuje format danych w usłudze Azure Cosmos DB

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

Migrowanie kodu

Ten artykuł obejmuje migrowanie kodu aplikacji do usługi Azure Cosmos DB, co jest krytycznym aspektem migracji bazy danych. Aby ułatwić zmniejszenie krzywej uczenia się, poniższe sekcje zawierają porównanie kodu równoległego między bazą danych Amazon DynamoDB i równoważnym fragmentem kodu usługi Azure Cosmos DB.

Aby pobrać kod źródłowy, sklonuj następujące repozytorium:

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

Wymagania wstępne

  • .NET Framework 4.7.2
  • Najnowsza wersja programu Visual Studio z pakietem roboczym Programowanie na platformie Azure. Możesz rozpocząć pracę z bezpłatnym środowiskiem IDE programu Visual Studio Community . Włącz obciążenie programowanie na platformie Azure podczas konfigurowania programu Visual Studio.
  • Dostęp do konta usługi Azure Cosmos DB for NoSQL
  • Lokalna instalacja bazy danych Amazon DynamoDB
  • Java 8
  • Uruchom pobraną wersję bazy danych Amazon DynamoDB na porcie 8000 (można zmienić i skonfigurować kod)

Konfigurowanie kodu

Dodaj następujący element "Pakiet NuGet" do projektu:

Install-Package Microsoft.Azure.Cosmos

Nawiązywanie połączenia

DynamoDB:

W usłudze Amazon DynamoDB do nawiązania połączenia służy następujący kod:

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

Azure Cosmos DB:

Aby połączyć usługę Azure Cosmos DB, zaktualizuj kod do:

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

Optymalizowanie połączenia w usłudze Azure Cosmos DB

Za pomocą usługi Azure Cosmos DB możesz użyć następujących opcji, aby zoptymalizować połączenie:

  • ConnectionMode — użyj trybu bezpośredniego połączenia, aby nawiązać połączenie z węzłami danych w usłudze Azure Cosmos DB. Użyj trybu bramy tylko do inicjowania i buforowania adresów logicznych i odświeżania aktualizacji. Aby uzyskać więcej informacji, zobacz tryby łączności.

  • ApplicationRegion — ta opcja służy do ustawiania preferowanego regionu replikowanego geograficznie, który jest używany do interakcji z usługą Azure Cosmos DB. Aby uzyskać więcej informacji, zobacz Dystrybucja globalna.

  • ConsistencyLevel — ta opcja służy do zastępowania domyślnego poziomu spójności. Aby uzyskać więcej informacji, zobacz poziomy spójności.

  • BulkExecutionMode — ta opcja służy do wykonywania operacji zbiorczych przez ustawienie właściwości AllowBulkExecution na wartość true. Aby uzyskać więcej informacji, zobacz importowanie zbiorcze.

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

Tworzenie kontenera

DynamoDB:

Aby zapisać dane w bazie danych Amazon DynamoDB, musisz najpierw utworzyć tabelę. W procesie tworzenia tabeli; należy zdefiniować schemat, typ klucza i atrybuty, jak pokazano w poniższym kodzie:

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

W usłudze Amazon DynamoDB należy aprowizować jednostki obliczeniowe odczytu i jednostki obliczeniowe zapisu. Podczas gdy w usłudze Azure Cosmos DB określasz przepływność jako jednostki żądań (RU/s), która może być używana w przypadku dowolnych operacji dynamicznie. Dane są zorganizowane jako baza danych —> kontener —> element. Przepływność można określić na poziomie bazy danych lub na poziomie kolekcji lub obu tych poziomach.

Aby utworzyć bazę danych:

await client_cosmosDB.CreateDatabaseIfNotExistsAsync(movies_table_name);

Aby utworzyć kontener:

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

Ładowanie danych

DynamoDB:

Poniższy kod pokazuje, jak załadować dane w usłudze Amazon DynamoDB. Aplikacja moviesArray składa się z listy dokumentów JSON, a następnie należy wykonać iterację i załadować dokument JSON do bazy danych 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:

W usłudze Azure Cosmos DB możesz wybrać opcję przesyłania strumieniowego i zapisu za pomocą polecenia moviesContainer.CreateItemStreamAsync(). Jednak w tym przykładzie kod JSON zostanie zdeserializowany do typu MovieModel , aby zademonstrować funkcję rzutowania typów. Kod jest wielowątkowy, który będzie używać rozproszonej architektury usługi Azure Cosmos DB i przyspieszyć ładowanie:

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

Tworzenie dokumentu

DynamoDB:

Pisanie nowego dokumentu w bazie danych Amazon DynamoDB nie jest bezpieczne. W poniższym przykładzie jako typ dokumentu użyto elementu newItem:

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

Azure Cosmos DB:

Usługa Azure Cosmos DB zapewnia bezpieczeństwo typów za pośrednictwem modelu danych. Używamy modelu danych o nazwie "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);
    }
}

W usłudze Azure Cosmos DB newItem będzie 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;

Odczytywanie dokumentu

DynamoDB:

Aby przeczytać w bazie danych Amazon DynamoDB, należy zdefiniować elementy pierwotne:

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

Jednak w przypadku usługi Azure Cosmos DB zapytanie jest naturalne (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;
    }

Kolekcja dokumentów w powyższym przykładzie będzie:

  • bezpieczny typ
  • podaj naturalną opcję zapytania.

Aktualizowanie elementu

DynamoDB: Aby zaktualizować element w bazie danych Amazon DynamoDB:

updateResponse = await client.UpdateItemAsync( updateRequest );

Azure Cosmos DB:

W usłudze Azure Cosmos DB aktualizacja będzie traktowana jako operacja Upsert, co oznacza wstawienie dokumentu, jeśli nie istnieje:

await moviesContainer.UpsertItemAsync<MovieModel>(updatedMovieModel);

Usuwanie dokumentu

DynamoDB:

Aby usunąć element w bazie danych Amazon DynamoDB, należy ponownie spaść na elementy pierwotne:

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:

W usłudze Azure Cosmos DB możemy pobrać dokument i usunąć je asynchronicznie:

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

Dokumenty z zapytaniami

DynamoDB:

W usłudze Amazon DynamoDB funkcje interfejsu API są wymagane do wykonywania zapytań dotyczących danych:

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:

W usłudze Azure Cosmos DB można wykonywać projekcję i filtrować wewnątrz prostego zapytania SQL:

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

W przypadku operacji zakresu, na przykład "między", należy przeprowadzić skanowanie w bazie danych 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( );

W usłudze Azure Cosmos DB można użyć zapytania SQL i instrukcji jednowierszowej:

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

Usuwanie kontenera

DynamoDB:

Aby usunąć tabelę w bazie danych Amazon DynamoDB, możesz określić:

client.DeleteTableAsync( tableName );

Azure Cosmos DB:

Aby usunąć kolekcję w usłudze Azure Cosmos DB, możesz określić:

await moviesContainer.DeleteContainerAsync();

Następnie usuń bazę danych również, jeśli potrzebujesz:

await cosmosDatabase.DeleteAsync();

Jak widać, usługa Azure Cosmos DB obsługuje zapytania naturalne (SQL), operacje są asynchroniczne i znacznie łatwiejsze. Możesz łatwo migrować złożony kod do usługi Azure Cosmos DB, co staje się prostsze po migracji.

Następne kroki