我需要将Azure Table Storage中的一个表中的记录的整个分区从Partition1移植到Partition2。成千上万,即使不是数百万。
我知道在Azure表存储中无法将实体从一个分区移植到另一个分区,您需要删除旧分区并插入一个更新的PartitionKey
,所以我的任务是做许多记录也一样。
有标准的东西吗?
我提出了以下解决方案(简化):
public async Task Migrate(string oldPartition, string newPartition)
{
TableContinuationToken token = null;
List<Task> migrationTasks = new List<Task>();
do
{
TableQuerySegment<MyTableEntity> entries = await GetEntriesSegment(
oldPartition,
token);
token = entries.ContinuationToken;
migrationTasks.Add(MigrateEntries(entries, newPartition));
} while (token != null)
await Task.WhenAll(migrationTasks);
}
private async Task MigrateEntries(IEnumerable<MyTableEntity> entries, string newPartition)
{
await Task.WhenAll(
InsertInBatches(entries.Select(
e => GetEntryWithUpdatedPartitionKey(e, newPartition)),
DeleteInBatches(entries));
}
GetEntriesSegment
包装逻辑以访问表并获取段GetEntryWithUpdatedPartitionKey
只是将MyTableEntity
类型的一个对象中的所有字段复制到新创建的字段中,但使用不同的PartitionKey
InsertInBatches
负责将条目集合拆分为100个批次(Azure表存储限制)并并行执行批量插入(通过另外一个await Task.WhenAll(insertTasks)
)DeleteInBatches
负责将条目集合拆分为100个批次(Azure表存储限制)并对所有项目进行批量删除(通过另外一个await Task.WhenAll(deleteTasks)
)我的主要目标是平行一切。即,应该读取新条目,同时删除已读取的条目并插入新条目。
这个解决方案看起来合理吗?您是否知道任何经过时间验证(经过良好测试,用于生产项目)的替代方案?
答案 0 :(得分:1)
你能试试https://msdn.microsoft.com/library/hh228603.aspx
吗?任务并行库(TPL)提供数据流组件,以帮助提高启用并发的应用程序的健壮性。
答案 1 :(得分:0)
您可以使用TPL Dataflow库来完成此任务,该库还可以优雅地处理将操作批处理为每个表批处理100个操作的要求。我还想强调一下,这段代码不会阻止等待读取整个分区,而是一次流一个批处理。这就是Dataflow库为您提供的功能。
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);
}
您可以这样使用它:
CloudTable table = GetCloudTable();
await MigratePartitionAsync<MyTableEntityClass>(table, "OldPartitionKey", "NewPartitionKey");