前提条件
- 使うAPIはSQLAPI
- RUはContainerに対して割り当てを行い、AutoScaleとします。
1ドキュメント当たりの制限
- CosmosDBには1つのITEM当たり2MBの制約があります。参照
- 2MBのJSONを書き込んだ場合どのようなエラーが出るかを確認します。
サンプルコード
- C#で以下のようなコードを作成しました。
sample1.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using Microsoft.Azure.Cosmos;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace ConsoleApp1
{
class Program
{
private static string connectString =
"XXXXXXXXX";
async static Task Main(string[] args)
{
var data = CreateData(2 * 1024 * 1024);
data.Partitionkey = "Part1";
using (var client = new CosmosClient(connectString, new CosmosClientOptions()))
{
var container = client.GetContainer("dbname", "containername");
try
{
await container.CreateItemAsync<TodoLists>(data, new PartitionKey(data.Partitionkey));
}
catch (CosmosException e)
{
Console.WriteLine($"message={e.Message}");
throw;
}
}
}
private static TodoLists CreateData(int limitSize)
{
var str = "";
var retData =new TodoLists();
var count = 0;
var serializer = JsonSerializer.CreateDefault();
using (var stream = new MemoryStream())
{
var writer = new JsonTextWriter(new StreamWriter(stream));
while (stream.Length <= limitSize)
{
count++;
retData.TodoDatas.Add(new TodoData
{
UserId = Guid.NewGuid().ToString(),
UserName = $"Taro {count}",
Title = $"Title {count}",
Contents = $"Contents {count}"
});
stream.Position = 0;
serializer.Serialize(writer,retData);
}
}
return retData;
}
}
public class TodoData
{
public string UserId { get; set; }
public string UserName { get; set; }
public string Title { get; set; }
public string Contents { get; set; }
}
public class TodoLists
{
[JsonProperty("id")]
public string Id { get; set; } = Guid.NewGuid().ToString();
public string Partitionkey { get; set; }
public IList<TodoData> TodoDatas { get; set; } = new List<TodoData>();
}
}
結果
1パーティション当たりの制限
-
最初のころCosmosDBのパーティションの設計を失敗しました、別の案件でも結構見かけました。(論理) パーティションあたりの最大ストレージ20GB昔は12GBだったような気がします。参照
-
注目したいのは下記です
- すべての項目にわたる、(論理) パーティションあたりの最大ストレージ20GB
- 個別の (論理) パーティション キーの最大数 無制限
これは、1パーティション当たり20GBを超えないパーティションキーを設定した場合に成り立ちます。パーティションKEYの最大数は無制限なので大丈夫です。
パーティションKEY設計に対する現時点での考察
- 20GBの制約があるため、大容量を要求する案件では20GBを超えない且つ均等分散をするようなKEY設計場合によっては複合KEY(性別+年代)とかを検討する。
- IDでしかアクセスしないようなコンテナはIDをパーティションKEYでもいいと思われる。
- クロスパーティションで検索するようになってしまう項目は設定しない。
1パーティション当たり20GBまで書き込んでみる
- C#で以下のようなコードを作成し20GBまで書き込む
sample2.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using Microsoft.Azure.Cosmos;
using System.Text;
using System.Threading.Tasks;
using System.Xml.XPath;
using Newtonsoft.Json;
namespace ConsoleApp1
{
class Program
{
private static BatchStatus status = new BatchStatus();
private static string connectString =
"XXXXXXXX";
async static Task Main(string[] args)
{
using (var client = new CosmosClient(connectString, new CosmosClientOptions { RequestTimeout = TimeSpan.FromMinutes(10), AllowBulkExecution = true, MaxRetryAttemptsOnRateLimitedRequests = int.MaxValue, MaxRetryWaitTimeOnRateLimitedRequests = TimeSpan.FromMinutes(10) }))
{
var container = client.GetContainer("dbname", "container1");
for (int count2 = 0; count2 < 40; count2++)
{
var tasks = new List<Task>();
for (var count = 0; count < 2000; count++)
{
var data = CreateData();
data.Partitionkey = "Part1";
tasks.Add(container.CreateItemAsync(data, new PartitionKey(data.Partitionkey)).ContinueWith(response =>
{
lock (status)
{
if (!response.IsCompletedSuccessfully)
{
var tempex = response.Exception.InnerExceptions[0] as CosmosException;
Console.WriteLine($"error {tempex.ToString()}");
status.Error++;
}
else
{
Console.WriteLine($" done RU use {response.Result.RequestCharge}");
status.Success++;
}
}
Console.WriteLine($"Success ={status.Success} Error = {status.Error}");
}));
}
await Task.WhenAll(tasks);
}
}
}
private static TodoLists CreateData()
{
var retData = new TodoLists();
for (var count = 1; count < 4273; count++)
{
retData.TodoDatas.Add(new TodoData
{
UserId = Guid.NewGuid().ToString(),
UserName = $"Taro {count}",
Title = $"Title {count}",
Contents = $"Contents {count}"
});
}
return retData;
}
}
public class TodoData
{
public string UserId { get; set; }
public string UserName { get; set; }
public string Title { get; set; }
public string Contents { get; set; }
}
public class TodoLists
{
[JsonProperty("id")]
public string Id { get; set; } = Guid.NewGuid().ToString();
public string Partitionkey { get; set; }
public IList<TodoData> TodoDatas { get; set; } = new List<TodoData>();
}
public class BatchStatus
{
public int Success { get; set; } = 0;
public int Error { get; set; } = 0;
}
}
結果
- 前述のSample1.cs を実行してみてどのようなエラーが出るか確認してみる。
- パーティションKEYが20GBを超えて書き込みが行えないことが確認できました。
- また、この状態で書き込み時に使用したSample2.csで実行するとエラーが下記のようになり、Reasonが消えてしまう。これはBulkInsertの問題なのか、別の方法を使えば取得できるのかはわからないので、今後調べる。
物理パーティションの最大サイズ
- 1つの物理パーティションは50GBまでです。参照
- 論理パーティションは物理パーティションにマッピングされて格納されます。複数の論理パーティション50GBを超えない範囲で物理パーティションにマッピングされます。同一論理パーティションのデータは1つの物理パーティションに格納されます。物理パーティションはまたぎません。論理パーティションは20GBの制約があります。20GBを超えない範囲で物理パーティションにマッピングされ、複数の論理パーティションで50GBを超える場合は次の物理パーティションが作成されて、CoSMOSDBで判断され均等にデータが分散されます。またAzureポータルで指定されたRUは物理パーティションで分割されるイメージです。
物理パーティション50GBを超えて書き込んでみる。
- C#で以下のようなコードを作成し50GBまで書き込む
sample3.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using Microsoft.Azure.Cosmos;
using System.Text;
using System.Threading.Tasks;
using System.Xml.XPath;
using Newtonsoft.Json;
namespace ConsoleApp1
{
class Program
{
private static BatchStatus status = new BatchStatus();
private static string connectString =
"XXXXXXXXXXXXXXX";
async static Task Main(string[] args)
{
using (var client = new CosmosClient(connectString, new CosmosClientOptions { RequestTimeout = TimeSpan.FromMinutes(10), AllowBulkExecution = true, MaxRetryAttemptsOnRateLimitedRequests = int.MaxValue, MaxRetryWaitTimeOnRateLimitedRequests = TimeSpan.FromMinutes(10) }))
{
var tasks = new List<Task>();
var container = client.GetContainer("dbname", "containername");
for (int count2 = 0; count2 < 80; count2++)
{
tasks.Clear();
foreach (var partkey in new []{ "Part1","Part2","Part3","Part4", "Part5", "Part6" })
{
for (var count = 0; count < 300; count++)
{
var data = CreateData();
data.Partitionkey = partkey;
tasks.Add(container.CreateItemAsync(data, new PartitionKey(data.Partitionkey)).ContinueWith(response =>
{
if (!response.IsCompletedSuccessfully)
{
var tempex = response.Exception.InnerExceptions[0] as CosmosException;
Console.WriteLine($"error {tempex.Message} ");
lock (status)
{
status.Error++;
}
}
else
{
Console.WriteLine($" done RU use {response.Result.RequestCharge}");
lock (status)
{
status.Success++;
}
}
Console.WriteLine($"Success ={status.Success} Error = {status.Error}");
}));
}
}
await Task.WhenAll(tasks);
}
}
}
private static TodoLists CreateData()
{
var retData = new TodoLists();
for (var count = 1; count < 8546; count++)
{
retData.TodoDatas.Add(new TodoData
{
UserId = Guid.NewGuid().ToString(),
UserName = $"Taro {count}",
Title = $"Title {count}",
Contents = $"Contents {count}"
});
}
return retData;
}
}
public class TodoData
{
public string UserId { get; set; }
public string UserName { get; set; }
public string Title { get; set; }
public string Contents { get; set; }
}
public class TodoLists
{
[JsonProperty("id")]
public string Id { get; set; } = Guid.NewGuid().ToString();
public string Partitionkey { get; set; }
public IList<TodoData> TodoDatas { get; set; } = new List<TodoData>();
}
public class BatchStatus
{
public int Success { get; set; } = 0;
public int Error { get; set; } = 0;
}
}
書き込み結果
- 物理パーティションの状況はAzureポータルから確認できます。また、このようにどのパーティションKEYが割り当てられたか上位3位まで確認可能です。
- 物理パーティションにどのKEYをマッピングするのかは決められません。
- またこの場合指定したRUは物理パーティション間で均等に割り当てられます。
- RUの最低は400RU 、AutoScaleの場合は4000RUからですが、この下減も変更されてしまいます。4000RUから自動で変更されていました。6000RUへ
- そして4000RUへ戻そうとするとエラーが起きます。
感想・まとめ
- CosmosDBの気になる部分の制限事項を確認してみました。
- 気になる部分はもう少しあるので今後も確認していきたい。