ここ一年程、新規製品の開発でAzure CosmosDBに触れることができました。
CosmosDBはMicrosoft Azureで手軽に使用できるNoSQLサービスですが、
いざリリースしてみると消費RUが想定以上となり金額面で苦労するケースがありそうです。
今回はCosmosDBの各処理毎のRU消費についてまとめてみようと思います。
- 前提
- ReadItemAsync()で取得した場合
- ReadManyItemAsync()で取得した場合
- GetItemLinqQueryable()で取得した場合
- GetItemLinqQueryable()でデータの一部を取得した場合
- CreateItemAsync()でのデータ追加した場合
- ReplaceItemAsync()でデータ更新した場合
- PatchItemAsync()でデータ更新した場合
- DeleteItemAsync()でデータ削除した場合
- まとめ
- おわりに
前提
サンプルとして検証したデータは以下のような形式になっています。
階層パーティションとしてParentId、Idが設定されているイメージです。
public class SampleDataModel { public string ParentId{ get; set; } public string Id { get; set; } public string LinkId{ get; set; } public string BaseSettingId { get; set; } public SampleDataSetting { get; set; }; public SampleTextItem TextItem { get; set; }; public SampleLayoutItem[] LayoutItems{ get; set; } = []; }
また、取得時は1件のみ取得と375件取得の2種類を計測しています。
( 375という数字に意味はありません(^^; )
ReadItemAsync()で取得した場合
1件取得:1.05 RU
375件取得:393.75 RU
// Sample Code var pk = new PartitionKeyBuilder() .Add(parentId.ToString()) .Add(id.ToString()).Build(); var res = await _container.ReadItemAsync<SampleDataModel>(id.ToString(), pk);
ReadManyItemAsync()で取得した場合
375件取得:137.05
// Sample Code var items = ids.Select(id => { var pk = new PartitionKeyBuilder() .Add(parentId.ToString()) .Add(id.ToString()).Build(); return (id.ToString(), pk); }); var res = await _container.ReadManyItemsAsync<SampleDataModel>(items);
GetItemLinqQueryable()で取得した場合
1件取得:2.95 RU
375件取得:22.1 RU
// Sample Code var requestOpt = new QueryRequestOptions { PartitionKey = new PartitionKey(parentId.ToString()) }; var serializerOpt = new CosmosLinqSerializerOptions { PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase }; using var iterator = _container.GetItemLinqQueryable<SampleDataModel>( requestOptions: requestOpt, linqSerializerOptions: serializerOpt) .Where(q => q.ParentId == parentId) .ToFeedIterator(); var result = new List<SampleDataModel>(); while (iterator.HasMoreResults) { var res = await _container.ReadIteratorNextAsync(iterator); result.AddRange(res); }
GetItemLinqQueryable()でデータの一部を取得した場合
375件取得:17.87 RU
※ 必要なデータがピンポイントであればSELECT文を
使用することでRUを抑える事ができます。
// Sample Code var requestOpt = new QueryRequestOptions { PartitionKey = new PartitionKey(parentId.ToString()) }; var serializerOpt = new CosmosLinqSerializerOptions { PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase }; using var iterator = _container.GetItemLinqQueryable<SampleDataModel>( requestOptions: requestOpt, linqSerializerOptions: serializerOpt) .Where(q => q.ParentId == parentId) .Select(q => q.LinkId) .ToFeedIterator(); var result = new List<string>(); while (iterator.HasMoreResults) { var res = await _container.ReadIteratorNextAsync(iterator); result.AddRange(res); }
CreateItemAsync()でのデータ追加した場合
1件追加:35.14 RU
// Sample Code var pk = new PartitionKeyBuilder() .Add(item.ParentId.ToString()) .Add(item.Id.ToString()).Build(); var result = await _container.CreateItemAsync(item, pk);
ReplaceItemAsync()でデータ更新した場合
1件変更:13.05 RU
// Sample Code var pk = new PartitionKeyBuilder() .Add(item.ParentId.ToString()) .Add(item.Id.ToString()).Build(); var result = await _container.ReplaceItemAsync(item, item.Id.ToString(), pk);
PatchItemAsync()でデータ更新した場合
1件変更:13.6 RU
// Sample Code var pk = new PartitionKeyBuilder() .Add(parentId.ToString()) .Add(id.ToString()).Build(); var result = await _container.PatchItemAsync<SampleDataModel>(id.ToString(), pk, patchOperations);
DeleteItemAsync()でデータ削除した場合
1件削除:35.05 RU
// Sample Code await _container.DeleteItemAsync<SampleDataModel>(strId, new PartitionKey(id.ToString()));
まとめ
扱うデータの構造による影響はあると思いますが、今回のケースでは上記表のような結果となりました。
GetItemLinqQueryable()の複数件取得が妙に消費RUが少ない結果となりましたが、取得結果としては問題ないことを確認しています。
今回の検証をまとめると、、、
① 1件のみのデータ取得はReadItemAsync()がお勧め
② 複数件の場合はGetItemLinqQueryable()の消費RUが少なそう(?)
※これについてはもう少し検証したいですね
③ 追加・変更・削除のRU消費は以下の関係で、
Patchは部分更新だけど処理が軽いわけではない
Create == Delete >> Replace == Patch
おわりに
KENTEMでは、様々な拠点でエンジニアを大募集しています! 建設×ITにご興味頂いた方は、是非下記のリンクからご応募ください。 recruit.kentem.jp career.kentem.jp