.net 在表存储分区之间移植实体

.net 在表存储分区之间移植实体,.net,async-await,task-parallel-library,azure-table-storage,tpl-dataflow,.net,Async Await,Task Parallel Library,Azure Table Storage,Tpl Dataflow,我需要将Azure表存储中表中的整个记录分区从分区1移植到分区2。成千上万,如果不是数百万的话 我知道在Azure Table Storage中无法将实体从一个分区移植到另一个分区,您需要删除旧的分区并插入一个新的分区,并使用更新的PartitionKey,因此我的任务是对许多记录执行相同的操作 有什么标准吗 我提出了以下解决方案(简化): 公共异步任务迁移(字符串oldPartition、字符串newPartition) { TableContinuationToken=空; List mig

我需要将Azure表存储中表中的整个记录分区从分区1移植到分区2。成千上万,如果不是数百万的话

我知道在Azure Table Storage中无法将实体从一个分区移植到另一个分区,您需要删除旧的分区并插入一个新的分区,并使用更新的
PartitionKey
,因此我的任务是对许多记录执行相同的操作

有什么标准吗

我提出了以下解决方案(简化):

公共异步任务迁移(字符串oldPartition、字符串newPartition)
{
TableContinuationToken=空;
List migrationTasks=new List();
做
{
TableQuerySegment条目=等待GetEntriesegment(
旧分区,
代币);
token=entries.ContinuationToken;
添加(MigrateEntries,newPartition);
}while(令牌!=null)
等待任务。WhenAll(迁移任务);
}
专用异步任务迁移尝试(IEnumerable条目、字符串newPartition)
{
等待任务(
插入批次(entries.Select(
e=>GetEntryWithUpdatedPartitionKey(e,newPartition)),
删除批次(条目));
}
  • getEntrieseSegment
    包装访问表和获取段的逻辑
  • GetEntryWithUpdatedPartitionKey
    只需将MyTableEntity类型的一个对象中的所有字段复制到新创建的对象中,但使用不同的
    PartitionKey
  • InsertInBatches
    负责将条目集合拆分为100个批次(Azure表存储限制),并并行执行所有条目的批次插入(通过一个以上的
    wait任务。whalll(insertTasks)
    inside)
  • DeleteInBatches
    负责将条目集合拆分为100个批次(Azure表存储限制),并并行执行所有条目的批次删除(通过一个以上的
    wait Task.whalll(deleteTasks)
    内部)
我的主要目标是并行处理所有内容。即,在删除已读条目和插入新条目的同时,应读取新条目

这个解决方案看起来合理吗?您知道任何经时间验证(经过良好测试,用于生产项目)的替代方案吗?

您能试试吗


任务并行库(TPL)提供数据流组件以帮助增强支持并发的应用程序的健壮性。

您可以使用TPL数据流库来实现这一点,TPL数据流库还优雅地处理了将操作批处理为每个表批处理100个操作的要求。我还想强调的是,此代码不会阻止等待所有要读取的分区,而是一次流一批。这是数据流库提供给您的

public async Task MigratePartitionAsync<T>(CloudTable table, string oldPartitionKey, string newPartitionKey)
    where T : TableEntity, new()
{
    // batch up to 100 records per table operation
    var buffer = new BatchBlock<T>(100);

    // migrate the records
    var migrator = new ActionBlock<T[]>(async x => await MigrateRecordsAsync(table, x, newPartitionKey));

    // link the blocks and set them to propogate their completion
    buffer.LinkTo(migrator, new DataflowLinkOptions { PropagateCompletion = true });

    // read the old partition
    await ReadPartitionAsync(table, buffer, oldPartitionKey);

    await migrator.Completion;
}

public async Task ReadPartitionAsync<T>(CloudTable table, ITargetBlock<T> buffer, string partitionKey)
    where T : TableEntity, new()
{
    var results = table.CreateQuery<T>().Where(x => x.PartitionKey == partitionKey); 

    foreach (var record in results)
    {
        await buffer.SendAsync(record);
    }

    buffer.Complete();
}

public async Task MigrateRecordsAsync<T>(CloudTable table, IEnumerable<T> records, string newPartitionKey)
    where T : TableEntity, new()
{
    var deleteBatch = new TableBatchOperation();

    foreach (var element in records)
    {
        deleteBatch.Delete(element);
    }

    await table.ExecuteBatchAsync(deleteBatch);

    var insertBatch = new TableBatchOperation();

    foreach (var element in records)
    {
        element.PartitionKey = newPartitionKey;
        insertBatch.Insert(element);
    }

    await table.ExecuteBatchAsync(insertBatch);
}
公共异步任务MigratePartitionAsync(CloudTable表,字符串oldPartitionKey,字符串newPartitionKey) 其中T:TableEntity,new() { //每个表操作最多批处理100条记录 var缓冲区=新批处理块(100); //迁移记录 var migrator=newactionblock(异步x=>await MigrateRecordsAsync(表,x,newPartitionKey)); //链接块并设置它们以实现其完成 LinkTo(migrator,新的DataflowLinkOptions{PropagateCompletion=true}); //读取旧分区 等待ReadPartitionAsync(表、缓冲区、oldPartitionKey); 等待迁移者完成; } 公共异步任务ReadPartitionAsync(CloudTable表、ITargetBlock缓冲区、字符串partitionKey) 其中T:TableEntity,new() { var results=table.CreateQuery()。其中(x=>x.PartitionKey==PartitionKey); foreach(结果中的var记录) { wait buffer.sendaync(记录); } buffer.Complete(); } 公共异步任务MigrateRecordsAsync(CloudTable表、IEnumerable记录、字符串newPartitionKey) 其中T:TableEntity,new() { var deleteBatch=新建TableBatchOperation(); foreach(记录中的var元素) { deleteBatch.Delete(元素); } wait table.ExecuteBatchAsync(deleteBatch); var insertBatch=新建TableBatchOperation(); foreach(记录中的var元素) { element.PartitionKey=newPartitionKey; insertBatch.Insert(元素); } wait table.ExecuteBatchAsync(insertBatch); } 您可以这样使用它:

CloudTable table = GetCloudTable();

await MigratePartitionAsync<MyTableEntityClass>(table, "OldPartitionKey", "NewPartitionKey");
CloudTable=GetCloudTable();
等待MigratePartitionAsync(表“OldPartitionKey”、“NewPartitionKey”);

您看过了吗?谢谢,这绝对值得一试。我仍然想知道是否有任何现成的解决方案可以解决我的特定问题:Azure表存储中分区之间的数据迁移。好的,我现在正在尝试。我仍然不知道如何创建一个从表存储接收数据并立即将其传递给处理的块。I。e、 ,即使现在正在处理另一批,新线程也会开始处理新数据,同时我们继续从表存储中读取数据。
CloudTable table = GetCloudTable();

await MigratePartitionAsync<MyTableEntityClass>(table, "OldPartitionKey", "NewPartitionKey");