將您的應用程式從 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
不適用 Database
Table 集合
項目 文件
屬性 欄位
次要索引 次要索引
主索引鍵 – 資料分割索引鍵 分割區索引鍵
主索引鍵 – 排序索引鍵 非必要
串流 ChangeFeed
寫入計算單位 要求單位 (彈性,可用於讀取或寫入)
讀取計算單位 要求單位 (彈性,可用於讀取或寫入)
全域資料表 不需要。 您可以在佈建 Azure Cosmos DB 帳戶時直接選取區域 (稍後可以變更區域)

結構差異

相較於 DynamoDB,Azure Cosmos DB 具有較簡單的 JSON 結構。 下列範例顯示差異

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
  • 使用 Azure 開發工作負載的最新 Visual Studio。 您可以開始使用免費Visual Studio Community IDE。 在 Visual Studio 設定期間啟用 Azure 開發工作負載。
  • 存取 Azure Cosmos DB for NoSQL 帳戶
  • Amazon DynamoDB 的本機安裝
  • Java 8
  • 在連接埠 8000 執行 Amazon DynamoDB 的可下載版本 (您可以變更及設定程式碼)

設定您的程式碼

將下列「NuGet 套件」新增至您的專案:

Install-Package Microsoft.Azure.Azure Cosmos DB

建立連線

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("your connectionstring from the Azure portal");

將 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 中,您會將輸送量指定為要求單位 (RU/秒),以動態方式用於任何作業。 資料會組織成資料庫 --> 容器 --> 項目。 您可以指定資料庫層級和/或集合層級的輸送量。

若要建立資料庫:

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,在移轉之後會變得更簡單。

後續步驟