Поделиться через


Перенос приложения из Amazon DynamoDB в Azure Cosmos DB

ОБЛАСТЬ ПРИМЕНЕНИЯ: NoSQL

Azure Cosmos DB — это масштабируемая, глобально распределенная, полностью управляемая база данных. Она обеспечивает гарантированный доступ к данным с низкой задержкой. Дополнительные сведения об Azure Cosmos DB см. в этой обзорной статье. В этой статье описывается, как перенести приложение .NET из DynamoDB в Azure Cosmos DB с минимальным изменением кода.

Концептуальные различия

Ниже указаны основные концептуальные различия между Azure Cosmos DB и DynamoDB.

DynamoDB Azure Cosmos DB
Нет данных База данных
Таблица Коллекция
Товар Документ
Атрибут Поле
Вторичный индекс Вторичный индекс
Первичный ключ — ключ секции Ключ раздела
Первичный ключ — ключ сортировки Не требуется
Stream Канал изменений
Единица вычислений для записи Единица запроса (гибкая, может использоваться для операций как чтения, так и записи)
Единица вычислений для чтения Единица запроса (гибкая, может использоваться для операций как чтения, так и записи)
Глобальные таблицы Не требуется. Вы можете напрямую выбрать регион при подготовке учетной записи Azure Cosmos DB (вы можете изменить регион позже).

Структурные различия

У Azure Cosmos DB более простая структура JSON по сравнению с DynamoDB. Различия можно увидеть в примере ниже

DynamoDB.

Следующий объект JSON представляет формат данных в 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.

Следующий объект JSON представляет формат данных в Azure Cosmos DB

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

Перенос кода

В этой статье рассматривается адаптация кода приложения для Azure Cosmos DB — важнейший аспект переноса базы данных. Чтобы сократить для вас время изучения этого вопроса, в последующих разделах фрагменты кода для Amazon DynamoDB будут сравниваться с эквивалентными фрагментами кода для Azure Cosmos DB.

Чтобы скачать исходный код, выполните клонирование следующего репозитория.

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

Предварительные требования

  • .NET Framework 4.7.2.
  • Visual Studio последней версии с рабочей нагрузкой разработки для Azure. Вы можете приступить к работе с бесплатной интегрированной среды разработки сообщества Visual Studio. При установке Visual Studio включите рабочую нагрузку разработки для Azure.
  • Доступ к учетной записи NoSQL в Azure Cosmos DB
  • Локально установленный экземпляр Amazon DynamoDB
  • Java 8
  • Загружаемая версия Amazon DynamoDB должна быть запущена на порте 8000 (вы можете соответствующим образом изменить и настроить код).

Настройка кода

Добавьте в проект следующий пакет NuGet.

Install-Package Microsoft.Azure.Cosmos

Установка подключения

DynamoDB.

В Amazon DynamoDB для подключения используется следующий код.

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

Azure Cosmos DB.

Для подключения к Azure Cosmos DB измените код следующим образом.

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

Оптимизация подключения в Azure Cosmos DB

В Azure Cosmos DB можно оптимизировать подключение с помощью следующих параметров.

  • ConnectionMode — использование режима прямого подключения для подключения к узлам данных в службе Azure Cosmos DB. Используйте режим шлюза только для инициализации и кэширования логических адресов и выполняйте обновление в случае изменений. Дополнительные сведения см. в статье о режимах подключения.

  • ApplicationRegion — этот параметр используется для задания предпочитаемого геореплицированного региона, который используется для взаимодействия с Azure Cosmos DB. Дополнительные сведения см. в статье о глобальном распределении.

  • ConsistencyLevel — этот параметр используется для переопределения уровня согласованности по умолчанию. Дополнительные сведения см. в статье об уровнях согласованности.

  • BulkExecutionMode — этот параметр используется для выполнения массовых операций путем установки для свойства AllowBulkExecution значения true. Дополнительные сведения см. в статье о массовом импорте.

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

Создание контейнера

DynamoDB.

Чтобы сохранить данные в Amazon DynamoDB, сначала необходимо создать таблицу. При ее создании вам необходимо определить схему, тип ключа и атрибуты, как показано в следующем коде.

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

В Amazon DynamoDB необходимо подготовить единицы вычислений для чтения и единицы вычислений для записи. В Azure Cosmos DB вы указываете пропускную способность в виде единиц запросов (единиц запросов в секунду), которые можно динамически использовать для любых операций. Данные организованы следующим образом: база данных --> контейнер --> элемент. Пропускную способность можно задать на уровне базы данных, на уровне сбора или на обоих этих уровнях.

Создание базы данных:

await client_cosmosDB.CreateDatabaseIfNotExistsAsync(movies_table_name);

Создание контейнера:

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

Загрузка данных

DynamoDB.

В приведенном ниже коде показано, как необходимо загружать данные в Amazon DynamoDB. Объект moviesArray содержит список документов JSON, по элементам которого необходимо последовательно пройти и загрузить каждый соответствующий документ JSON в 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.

В Azure Cosmos DB можно выбрать потоковую передачу и запись с помощью метода moviesContainer.CreateItemStreamAsync(). Однако в этом примере JSON будет десериализирован в тип MovieModel, чтобы продемонстрировать функцию приведения типов. Код является многопоточным. Благодаря этому будет использоваться распределенная архитектура Azure Cosmos DB и загрузка будет происходить быстрее.

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

Создание документа

DynamoDB.

Запись нового документа в Amazon DynamoDB не является типобезопасной. В следующем примере элемент newItem используется в качестве типа документа.

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

Azure Cosmos DB.

Azure Cosmos DB обеспечивает безопасность типов с помощью модели данных. Мы используем модель данных с именем 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);
    }
}

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

Чтение документа

DynamoDB.

Для чтения данных в Amazon DynamoDB необходимо определить примитивы.

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

Однако в Azure Cosmos DB такой запрос является естественным (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;
    }

Коллекция документов в примере выше:

  • является типобезопасной;
  • предоставляет естественный параметр запроса.

Обновление элемента

DynamoDB: обновление элемента в Amazon DynamoDB:

updateResponse = await client.UpdateItemAsync( updateRequest );

Azure Cosmos DB.

В Azure Cosmos DB операция обновления рассматривается как операция Upsert — это означает, что документ будет вставлен, если он не существует.

await moviesContainer.UpsertItemAsync<MovieModel>(updatedMovieModel);

Удаление документа

DynamoDB.

Чтобы удалить элемент в Amazon DynamoDB, необходимо еще раз обратиться к примитивам.

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.

В Azure Cosmos DB можно асинхронно получить документ и удалить его.

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

Запрос документов

DynamoDB.

В Amazon DynamoDB для запроса данных необходимы функции API.

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.

В Azure Cosmos DB можно выполнить проекцию и фильтрацию внутри простого 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'))");

Для операций с диапазонами (например, BETWEEN) в 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( );

В Azure Cosmos DB можно использовать SQL-запрос и однострочную инструкцию.

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

Удаление контейнера

DynamoDB.

Для удаления таблицы в Amazon DynamoDB можно указать следующую инструкцию.

client.DeleteTableAsync( tableName );

Azure Cosmos DB.

Для удаления коллекции в Azure Cosmos DB укажите следующую инструкцию.

await moviesContainer.DeleteContainerAsync();

Затем можно удалить базу данных, если это требуется.

await cosmosDatabase.DeleteAsync();

Как видите, Azure Cosmos DB поддерживает естественные запросы (SQL), а операции являются асинхронными и более простыми. Сложный код можно легко адаптировать для Azure Cosmos DB, и после этого он станет проще.

Next Steps