Azure Cosmos DB 的重写分区键策略
Write-heavy partition key strategy for Azure Cosmos DB
我们在生产中使用 CosmosDB 来存储 HTTP request/response 审计数据。该数据的结构大致如下:
{
"id": "5ff4c51d3a7a47c0b5697520ae024769",
"Timestamp": "2019-06-27T10:08:03.2123924+00:00",
"Source": "Microservice",
"Origin": "Client",
"User": "SOME-USER",
"Uri": "GET /some/url",
"NormalizedUri": "GET /SOME/URL",
"UserAgent": "okhttp/3.10.0",
"Client": "0.XX.0-ssffgg;8.1.0;samsung;SM-G390F",
"ClientAppVersion": "XX-ssffgg",
"ClientAndroidVersion": "8.1.0",
"ClientManufacturer": "samsung",
"ClientModel": "SM-G390F",
"ResponseCode": "OK",
"TrackingId": "739f22d01987470591556468213651e9",
"Response": "[ REDACTED ], <— Usually quite long (thousands of chars)
"PartitionKey": 45,
"InstanceVersion": 1,
"_rid": "TIFzALOuulIEAAAAAACACA==",
"_self": "dbs/TIFzAA==/colls/TIFzALOuulI=/docs/TIFzALOuulIEAAAAAACACA==/",
"_etag": "\"0d00c779-0000-0d00-0000-5d1495830000\"",
"_attachments": "attachments/",
"_ts": 1561630083
}
我们目前每天编写大约 150,000 - 200,000 个与上述类似的文档,其中 /PartitionKey
作为在容器上配置的分区键路径。 PartitionKey 的值是 C#.net 中随机生成的一个数字,介于 0 和 999 之间。
但是,我们看到日常热点中单个物理分区最多可以达到 2.5K - 4.5K RU/s,而其他的非常低(大约 200 RU/s)。这会影响成本,因为我们需要为最大的使用分区提供吞吐量。
第二个因素是我们要存储相当多的数据,接近 1TB 的文档,而且我们每天都会增加几 GB。因此,我们目前有大约 40 个物理分区。
结合这两个因素意味着我们最终必须至少准备 120,000 - 184,000 RU/s。
我应该提一下,我们几乎不需要查询这些数据;除了偶尔在 Cosmos 数据资源管理器中临时手动构建的查询。
我的问题是...通过简单地使用“id”列作为我们的分区键(或随机生成的 GUID),我们在 RU/s 要求和数据分布方面会更好吗- 然后设置合理的 TTL,这样我们就没有持续增长的数据集?
我知道这需要我们重新创建集合。
非常感谢。
Max throughput per physical partition
虽然使用 id 或 GUID 会比现在的随机数提供更好的基数,但您 运行 的任何查询都将非常昂贵,因为它总是跨分区且数量巨大数据。
我认为更好的选择是使用组合多个属性的合成键,这些属性既具有高基数又用于查询数据。可以在这里了解更多信息,https://docs.microsoft.com/en-us/azure/cosmos-db/synthetic-partition-keys
就 TTL 而言,我肯定会将其设置为您需要保留此数据的任何时间。 Cosmos 将使用未使用的吞吐量关闭数据,因此永远不会妨碍。
最后,您还应该考虑(如果您还没有)使用自定义索引策略并排除任何从未被查询过的路径。特别是 "response" 属性 因为你说它有数千个字符长。这可以在像您这样的写入密集型场景中节省大量 RU/s。
根据我的经验,我发现 cosmos 会随着新数据而退化。更多的数据意味着更多的物理分区。因此,您需要将更多的吞吐量分配给它们中的每一个。目前我们开始将旧数据归档到 blob 存储中,以避免此类问题并保持物理分区数量不变。我们使用 cosmos 作为热存储,然后旧数据去 blobs 存储作为冷存储。我们减少了分配给每个物理分区的 RU,从而节省了资金。
我们在生产中使用 CosmosDB 来存储 HTTP request/response 审计数据。该数据的结构大致如下:
{
"id": "5ff4c51d3a7a47c0b5697520ae024769",
"Timestamp": "2019-06-27T10:08:03.2123924+00:00",
"Source": "Microservice",
"Origin": "Client",
"User": "SOME-USER",
"Uri": "GET /some/url",
"NormalizedUri": "GET /SOME/URL",
"UserAgent": "okhttp/3.10.0",
"Client": "0.XX.0-ssffgg;8.1.0;samsung;SM-G390F",
"ClientAppVersion": "XX-ssffgg",
"ClientAndroidVersion": "8.1.0",
"ClientManufacturer": "samsung",
"ClientModel": "SM-G390F",
"ResponseCode": "OK",
"TrackingId": "739f22d01987470591556468213651e9",
"Response": "[ REDACTED ], <— Usually quite long (thousands of chars)
"PartitionKey": 45,
"InstanceVersion": 1,
"_rid": "TIFzALOuulIEAAAAAACACA==",
"_self": "dbs/TIFzAA==/colls/TIFzALOuulI=/docs/TIFzALOuulIEAAAAAACACA==/",
"_etag": "\"0d00c779-0000-0d00-0000-5d1495830000\"",
"_attachments": "attachments/",
"_ts": 1561630083
}
我们目前每天编写大约 150,000 - 200,000 个与上述类似的文档,其中 /PartitionKey
作为在容器上配置的分区键路径。 PartitionKey 的值是 C#.net 中随机生成的一个数字,介于 0 和 999 之间。
但是,我们看到日常热点中单个物理分区最多可以达到 2.5K - 4.5K RU/s,而其他的非常低(大约 200 RU/s)。这会影响成本,因为我们需要为最大的使用分区提供吞吐量。
第二个因素是我们要存储相当多的数据,接近 1TB 的文档,而且我们每天都会增加几 GB。因此,我们目前有大约 40 个物理分区。
结合这两个因素意味着我们最终必须至少准备 120,000 - 184,000 RU/s。
我应该提一下,我们几乎不需要查询这些数据;除了偶尔在 Cosmos 数据资源管理器中临时手动构建的查询。
我的问题是...通过简单地使用“id”列作为我们的分区键(或随机生成的 GUID),我们在 RU/s 要求和数据分布方面会更好吗- 然后设置合理的 TTL,这样我们就没有持续增长的数据集?
我知道这需要我们重新创建集合。
非常感谢。
Max throughput per physical partition
虽然使用 id 或 GUID 会比现在的随机数提供更好的基数,但您 运行 的任何查询都将非常昂贵,因为它总是跨分区且数量巨大数据。
我认为更好的选择是使用组合多个属性的合成键,这些属性既具有高基数又用于查询数据。可以在这里了解更多信息,https://docs.microsoft.com/en-us/azure/cosmos-db/synthetic-partition-keys
就 TTL 而言,我肯定会将其设置为您需要保留此数据的任何时间。 Cosmos 将使用未使用的吞吐量关闭数据,因此永远不会妨碍。
最后,您还应该考虑(如果您还没有)使用自定义索引策略并排除任何从未被查询过的路径。特别是 "response" 属性 因为你说它有数千个字符长。这可以在像您这样的写入密集型场景中节省大量 RU/s。
根据我的经验,我发现 cosmos 会随着新数据而退化。更多的数据意味着更多的物理分区。因此,您需要将更多的吞吐量分配给它们中的每一个。目前我们开始将旧数据归档到 blob 存储中,以避免此类问题并保持物理分区数量不变。我们使用 cosmos 作为热存储,然后旧数据去 blobs 存储作为冷存储。我们减少了分配给每个物理分区的 RU,从而节省了资金。