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


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

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

В этой статье описывается перенос приложения .NET из Amazon DynamoDB в Azure Cosmos DB с минимальными изменениями кода. Дополнительные сведения об Azure Cosmos DB см. в этой обзорной статье.

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

В следующей таблице перечислены основные основные различия между Azure Cosmos DB и DynamoDB:

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

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

Структура JSON Azure Cosmos DB проще, чем структура JSON в DynamoDB. В следующем примере показаны различия.

DynamoDB

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

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

С Artist ключом секции и SongTitle ключом сортировки.

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 Community. При установке Visual Studio включите рабочую нагрузку разработки для Azure.
  • Доступ к учетной записи Azure Cosmos DB для NoSQL.
  • Локальная установка 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);
}
catch { }

Azure Cosmos DB (облачная база данных)

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

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

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

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

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

DynamoDB

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

// movies_key_schema
public static List<KeySchemaElement> moviesKeySchema =
    new List<KeySchemaElement>
    {
        new KeySchemaElement
        {
            AttributeName = partitionKeyName,
            KeyType = "HASH"
        },
        new KeySchemaElement
        {
            AttributeName = sortKeyName,
            KeyType = "RANGE"
        },
    };

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

// movie_items_attributes
public static List<AttributeDefinition> movieItemsAttributes =
    new List<AttributeDefinition>
    {
        new AttributeDefinition
        {
            AttributeName = partitionKeyName,
            AttributeType = "N"
        },
        new AttributeDefinition
        {
            AttributeName = sortKeyName,
            AttributeType = "S"
        }
    };

CreateTableRequest request;
CreateTableResponse response;

// Build the 'CreateTableRequest' structure for the new table
request = new CreateTableRequest
{
    TableName             = tableName,
    AttributeDefinitions  = tableAttributes,
    KeySchema             = tableKeySchema,
    // 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 укажите пропускную способность в виде единиц запросов в секунду (ЕЗ/с). Вы можете динамически использовать RU/s для выполнения любых операций. Данные организованы как база данных, контейнер и элемент. Пропускную способность можно указать на уровне базы данных, на уровне коллекции или обоих.

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

await client_cosmosDB.CreateDatabaseIfNotExistsAsync(movies_table_name);

Чтобы создать контейнер, выполните приведенные действия.

await cosmosDatabase.CreateContainerIfNotExistsAsync(new ContainerProperties()
    {
        PartitionKeyPath = "/" + partitionKey,
        Id = newCollectionName
    },
    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;
    }
    catch { }
}

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)));
      if (i >= j)
      {
          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 тип документа:

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

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; }
    [JsonProperty("info")]
    public MovieInfo MovieInfo { get; set; }

    public MovieModel(string title, int year)
    {
        this.Title = title;
        this.Year = year;
    }
    public MovieModel() { }

    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 }
};
await moviesContainer.CreateItemAsync(movieModel, new Microsoft.Azure.Cosmos.PartitionKey(movieModel.Year));

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

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

Document movieRecord = await moviesTable.GetItemAsync(hash, range, token);

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;
  
Document deletedItem = await table.DeleteItemAsync(hash, range, deleteConfig);

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 c.year BETWEEN 1960 AND 1969");

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

DynamoDB

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

await client.DeleteTableAsync(tableName);

Azure Cosmos DB (облачная база данных)

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

await moviesContainer.DeleteContainerAsync();

При необходимости удалите базу данных:

await cosmosDatabase.DeleteAsync();

Сводка

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