DocumentDB - 分区



当您的数据库开始增长到超过 10GB 时,您可以通过简单地创建新的集合,然后将您的数据分散或分区到越来越多的集合中来扩展。

迟早,单个集合(容量为 10GB)将不足以容纳您的数据库。现在 10GB 可能听起来不是一个很大的数字,但请记住,我们存储的是 JSON 文档,它只是纯文本,即使考虑到索引的存储开销,您也可以在 10GB 中容纳大量纯文本文档。

在可扩展性方面,存储并不是唯一的关注点。S3 集合提供的集合最大吞吐量为每秒 2500 个请求单元。因此,如果您需要更高的吞吐量,那么您还需要通过使用多个集合进行分区来扩展。扩展分区也称为**水平分区**。

Azure DocumentDB 可以使用多种方法对数据进行分区。以下是最常见的策略 -

  • 溢出分区
  • 范围分区
  • 查找分区
  • 哈希分区

溢出分区

溢出分区是最简单的策略,因为它没有分区键。当您不确定很多事情时,它通常是一个不错的选择。您可能不知道您是否需要扩展到单个集合之外,或者您可能需要添加多少个集合,或者您可能需要添加的速度有多快。

  • 溢出分区从单个集合开始,并且没有分区键。

  • 集合开始增长,然后继续增长,然后继续增长,直到您开始接近 10GB 的限制。

  • 当您达到 90% 的容量时,您会溢出到一个新的集合并开始使用它来存储新文档。

  • 一旦您的数据库扩展到大量集合,您可能需要转向基于分区键的策略。

  • 当您这样做时,您需要通过根据您迁移到的任何策略将文档移动到不同的集合来重新平衡您的数据。

范围分区

最常见的策略之一是范围分区。使用这种方法,您可以确定文档分区键可能落在其中的值范围,并将文档定向到与该范围对应的集合。

  • 日期通常与此策略一起使用,您可以在其中创建一个集合来保存落在定义的日期范围内的文档。当您定义足够小的范围时,您有信心没有集合会超过其 10GB 的限制。例如,可能有一种情况是单个集合可以合理地处理整个月的文档。

  • 也可能是大多数用户都在查询当前数据,这将是本月或可能上个月的数据,但用户很少搜索更旧的数据。因此,您从 6 月开始使用 S3 集合,这是您可以购买的最昂贵的集合,并且可以提供您所能获得的最佳吞吐量。

  • 在 7 月,您购买另一个 S3 集合来存储 7 月份的数据,并且还将 6 月份的数据缩减到更便宜的 S2 集合。然后在 8 月,您获得另一个 S3 集合并将 7 月份缩减到 S2,并将 6 月份一直缩减到 S1。它会持续下去,月复一月,您始终保持当前数据可用以获得高吞吐量,而旧数据则以较低的吞吐量保持可用。

  • 只要查询提供了分区键,就只会查询需要查询的集合,而不是像溢出分区那样查询数据库中的所有集合。

查找分区

使用查找分区,您可以定义一个分区映射,该映射根据文档的分区键将文档路由到特定的集合。例如,您可以按区域进行分区。

  • 将所有美国文档存储在一个集合中,所有欧洲文档存储在另一个集合中,并将来自任何其他区域的所有文档存储在第三个集合中。

  • 使用此分区映射,查找分区解析器可以根据分区键(包含在每个文档中的区域属性)确定要创建文档的集合以及要查询的集合。

哈希分区

在哈希分区中,分区是根据哈希函数的值分配的,允许您在多个分区之间均匀分布请求和数据。

这通常用于对大量不同客户端生成或使用的产生的数据进行分区,并且对于存储用户配置文件、目录项等很有用。

让我们来看一个使用 .NET SDK 提供的 RangePartitionResolver 进行范围分区的简单示例。

**步骤 1** - 创建一个新的 DocumentClient,我们将在 CreateCollections 任务中创建两个集合。一个将包含用户 ID 以 A 到 M 开头的用户的文档,另一个将包含用户 ID 以 N 到 Z 开头的用户的文档。

private static async Task CreateCollections(DocumentClient client) {
   await client.CreateDocumentCollectionAsync(“dbs/myfirstdb”, new DocumentCollection {
      Id = “CollectionAM” }); 
		
   await client.CreateDocumentCollectionAsync(“dbs/myfirstdb”, new DocumentCollection {
      Id = “CollectionNZ” }); 
}

**步骤 2** - 为数据库注册范围解析器。

**步骤 3** - 创建一个新的 RangePartitionResolver<string>,这是我们的分区键的数据类型。构造函数采用两个参数,分区键的属性名称和一个字典,即分片映射或分区映射,它只是我们为解析器预定义的范围和相应集合的列表。

private static void RegisterRangeResolver(DocumentClient client) {

   //Note: \uffff is the largest UTF8 value, so M\ufff includes all strings that start with M.
		
   var resolver = new RangePartitionResolver<string>(
      "userId", new Dictionary<Range<string>, string>() {
      { new Range<string>("A", "M\uffff"), "dbs/myfirstdb/colls/CollectionAM" },
      { new Range<string>("N", "Z\uffff"), "dbs/myfirstdb/colls/CollectionNZ" },
   });
	
   client.PartitionResolvers["dbs/myfirstdb"] = resolver;
 }

这里有必要对最大可能的 UTF-8 值进行编码。否则,第一个范围将不匹配任何 Ms,除了单个 M,第二个范围中的 Z 也一样。因此,您可以将此处的编码值视为匹配分区键的通配符。

**步骤 4** - 创建解析器后,使用当前 DocumentClient 为数据库注册它。为此,只需将其分配给 PartitionResolver 的 dictionary 属性即可。

我们将针对数据库创建和查询文档,而不是像通常那样针对集合创建和查询文档,解析器将使用此映射将请求路由到相应的集合。

现在让我们创建一些文档。首先,我们将为 userId Kirk 创建一个,然后为 Spock 创建一个。

private static async Task CreateDocumentsAcrossPartitions(DocumentClient client) { 
   Console.WriteLine(); 
   Console.WriteLine("**** Create Documents Across Partitions ****");
	
   var kirkDocument = await client.CreateDocumentAsync("dbs/myfirstdb", new { userId =
      "Kirk", title = "Captain" }); 
   Console.WriteLine("Document 1: {0}", kirkDocument.Resource.SelfLink);
	
   var spockDocument = await client.CreateDocumentAsync("dbs/myfirstdb", new { userId =
      "Spock", title = "Science Officer" });		
   Console.WriteLine("Document 2: {0}", spockDocument.Resource.SelfLink); 
}

此处的第一个参数是数据库的自链接,而不是特定集合。如果没有分区解析器,这是不可能的,但有了它,它就可以无缝地工作。

两个文档都保存到数据库 myfirstdb 中,但我们知道 Kirk 存储在 A 到 M 的集合中,Spock 存储在 N 到 Z 的集合中,如果我们的 RangePartitionResolver 工作正常。

让我们从 CreateDocumentClient 任务中调用这些,如下面的代码所示。

private static async Task CreateDocumentClient() {
   // Create a new instance of the DocumentClient 
   using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
      await CreateCollections(client);  
      RegisterRangeResolver(client);  
      await CreateDocumentsAcrossPartitions(client); 
   } 
}

执行上述代码后,您将收到以下输出。

**** Create Documents Across Partitions **** 
Document 1: dbs/Ic8LAA==/colls/Ic8LAO2DxAA=/docs/Ic8LAO2DxAABAAAAAAAAAA==/ 
Document 2: dbs/Ic8LAA==/colls/Ic8LAP12QAE=/docs/Ic8LAP12QAEBAAAAAAAAAA==/

如您所见,这两个文档的自链接具有不同的资源 ID,因为它们存在于两个单独的集合中。

广告