Azure Table Storage の Entity Group Transaction でスループットが向上するのか試してみた
Azure Table Storage のパーティション設計に関するドキュメントを読んでいると、Entity Group Transaction に関する記述がありました。
この記事では Entity Group Transaction を使うとどれほどスループットが向上するのか試してみたので紹介します。
Entity Group Transaction とはなんぞや
まずはドキュメントで説明されている Entity Group Transaction について抜粋して紹介
An entity group transaction is a set of storage operations that are implemented atomically on entities that have the same PartitionKey value.
Entity group transactions improve throughput because they reduce the number of individual storage operations that must be submitted to Azure Table storage. Entity group transactions also provide an economic benefit. An entity group transaction is billed as a single storage operation regardless of how many storage operations it contains.
要約すると、
- Entity Group Transaction とは同じ PartitionKey を持つエンティティに対するストレージ操作のまとまりだよ
- Table Storage に対する個別の操作数を減らすことが出来るので、スループットが向上するよ
- Table Storage の課金はトランザクション数 (読み取りや書き込みなど個々の操作) にも関連するが、Entity Group Transaction は複数の操作を一つにまとめる事が出来るのでお得だよ
です。
Entity Group Transaction に含める事が出来る操作の最大数は 100 です。 このあたりの詳細は以下の公式ドキュメントを参照してください。
んで、Entity Group Transaction を使うとどれほどスループットが向上するのか気になったので試してみました。
Entity Group Transaction とスループットの向上
Azure.Data.Tables の TableClient を使ってみました。
の 2 つを試してみました。
書いたプログラムはこちらです。
[FunctionName("InsertEntityWithSamePartition")] public async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = "tenant/_samepartitionKey")] HttpRequest req, ExecutionContext executionContext, ILogger log) { log.LogInformation($"C# HTTP trigger {executionContext.FunctionName} processed a request."); var tableClient = _tableServiceClient.GetTableClient(SamePartitionTableName); var sw = new Stopwatch(); var elapsedTimes = new List<long>(100); var partitonKey = "samePartitionKey"; foreach(var _ in Enumerable.Range(0, 100)) { sw.Start(); foreach (var __ in Enumerable.Range(0,100)) { var entity = new SampleEntity { PartitionKey = partitonKey, RowKey = Guid.NewGuid().ToString() }; await tableClient.AddEntityAsync(entity); } sw.Stop(); elapsedTimes.Add(sw.ElapsedMilliseconds); } await WriteFile(executionContext.FunctionName, elapsedTimes); return new OkObjectResult("Success"); }
同一のパーティションに対して書き込みを続けるので、時間経過とともにスループットの変化を見てみたく 100 件書き込むごとに ElapsedMilliseconds
を取得したのですが、大きな変化はありませんでした。
続いてバッチ書き込みのプログラムです。
[FunctionName("BatchInsertEntitySamePartition")] public async Task<IActionResult> RunBatchInsertSamePartition( [HttpTrigger(AuthorizationLevel.Function, "post", Route = "tenant/_samepartitionKey/_batch")] HttpRequest req, ExecutionContext executionContext, ILogger log) { var tableClient = _tableServiceClient.GetTableClient(SamePartitionTableName); var sw = new Stopwatch(); var elapsedTimes = new List<long>(100); foreach (var i in Enumerable.Range(0, 100)) { var partitonKey = "partitionKey"; var batch = new List<TableTransactionAction>(100); sw.Start(); foreach (var _ in Enumerable.Range(0, 100)) { var entity = new SampleEntity { PartitionKey = partitonKey, RowKey = Guid.NewGuid().ToString(), }; batch.Add(new TableTransactionAction(TableTransactionActionType.Add, entity)); } await tableClient.SubmitTransactionAsync(batch); sw.Stop(); elapsedTimes.Add(sw.ElapsedMilliseconds); } await WriteFile(executionContext.FunctionName, elapsedTimes); return new ObjectResult("Success"); }
ファイルへの書き込みは同じメソッドを使っています。
結論だけ言うと 14 倍ほど速くなっていました。(数回やっての平均値とかはめんどくさかったので取ってないです)
気が向いたら PartitionKey が異なるものを混ぜるとどうなるか、100 件以上の操作をとりあつかうとどうなるか、とか見ようと思います。
あ、github はこちらです。