4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Azure CosmosDBの制限事項に関して纏めてみる

Posted at

前提条件

  1. 使うAPIはSQLAPI
  2. 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>();


    }
}

結果

  • 以下のようなエラーが出力されました。
    RequestLarge.PNG
  • ステータスコード413 RequestEntityTooLarge が返却されることがわかりました。

1パーティション当たりの制限

  • 最初のころCosmosDBのパーティションの設計を失敗しました、別の案件でも結構見かけました。(論理) パーティションあたりの最大ストレージ20GB昔は12GBだったような気がします。参照

  • 注目したいのは下記です

    • すべての項目にわたる、(論理) パーティションあたりの最大ストレージ20GB
    • 個別の (論理) パーティション キーの最大数 無制限

    Azure Cosmos DB では、無制限のストレージとスループットを提供します。

これは、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;
    }
}

  • 書き込んだ結果はAzureポータルから確認ができます。Part1というパーティションKEYで20GB書き込んでいることが確認できます。
    Cap1.PNG

結果

  • 前述のSample1.cs を実行してみてどのようなエラーが出るか確認してみる。
    PartOverError.PNG
  • パーティションKEYが20GBを超えて書き込みが行えないことが確認できました。
  • また、この状態で書き込み時に使用したSample2.csで実行するとエラーが下記のようになり、Reasonが消えてしまう。これはBulkInsertの問題なのか、別の方法を使えば取得できるのかはわからないので、今後調べる。
    image.png

物理パーティションの最大サイズ

  • 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位まで確認可能です。
    Part1.png
    Part2_LI.jpg
  • 物理パーティションにどのKEYをマッピングするのかは決められません。
  • またこの場合指定したRUは物理パーティション間で均等に割り当てられます。
  • RUの最低は400RU 、AutoScaleの場合は4000RUからですが、この下減も変更されてしまいます。4000RUから自動で変更されていました。6000RUへ
    RU.png
  • そして4000RUへ戻そうとするとエラーが起きます。
    Ru2.PNG

感想・まとめ

  • CosmosDBの気になる部分の制限事項を確認してみました。
  • 気になる部分はもう少しあるので今後も確認していきたい。
4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?