Microsoft Azure Cosmos DocumentDB 最佳读取查询性能
Microsoft Azure Cosmos DocumentDB Optimal Read Query Performance
我们已经在云中实现了 Azure CosmosDB(MongoDB 和 SQL API)数据库。通过java,我们想根据隐藏在MongoDB中的数据生成报告。我对读取查询的性能还不太满意,我想知道我当前的设置可以改进什么。
如前所述,我使用Java查询数据库。我使用 Microsoft Azure DocumentDB 库查询数据库:
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-documentdb</artifactId>
<version>1.16.2</version>
</dependency>
目前,我能够获得的最佳性能是在大约 20 秒内查询内存中的大约 38.000 个文档,配置了 50,000 RU/s(本地 cosmos 模拟器)。我真的很希望这个改进,因为我们可能很快就会查询数百万个文档。
我感觉我们存储数据的方式可能不是最优的。每个文件现在看起来如下:
{
"deviceid": "xxx",
"devicedata": {
"datetime": "2018-08-28T00:00:02.104Z",
"sensors": [
{
"p_A2": "93095",
"p_A3": "303883",
"p_batterycurrent": "4294967.10000",
"p_batterygauge": "38.27700",
"p_batteryvoltage": "13.59400",
** ... around 200 more key - value pairs ... **
}
]
},
"id": "aa5d3cf5-10fa-48dd-a0d2-a536284eddac",
"_rid": "PtEIANkbMQABAAAAAAAAAA==",
"_self": "dbs/PtEIAA==/colls/PtEIANkbMQA=/docs/PtEIANkbMQABAAAAAAAAAA==/",
"_etag": "\"00000000-0000-0000-4040-006a7f2501d4\"",
"_attachments": "attachments/",
"_ts": 1535619672
}
我们经常使用的查询如下所示:
SELECT c.deviceid,
c.devicedata.datetime,
c.devicedata.sensors[0].p_A2,
c.devicedata.sensors[0].p_A3,
c.devicedata.sensors[0].p_batterycurrent,
c.devicedata.sensors[0].s_humidity
FROM c
WHERE c.deviceid = 'xxx'
AND c.devicedata.datetime >= '2018-08-28T00:00:00.000Z'
AND c.devicedata.datetime < '2018-08-30T00:00:00.000Z'
order by c.devicedata.datetime desc
我根据 deviceId 减少了这些查询。因此,对于每个设备,我 运行 一个带有此查询的线程。这似乎比使用单个查询的单个线程快得多。
上面这样的查询大约需要 20 秒。
但是我注意到,如果我只查询 deviceid 和 devicedata.datetime,查询会在 2 秒内完成。从传感器列表中获取传感器数据似乎是一件非常困难的事情。如果我执行 select *(因此不过滤传感器数据),它也比我让 SQL API 过滤传感器时更快:大约 15 秒。
我的问题是,我可以做些什么来改进它?我的文档列表是否太长?有什么办法可以不同地设置它吗?传感器键值对不固定,可能因设备而异。
更多技术细节:
我有一个无限 collection,分区在 /deviceid 上。
我使用了 Azure 的标准索引策略(索引所有内容),并从中排除了传感器。
我已经尝试了这里描述的所有技巧:
https://docs.microsoft.com/en-us/azure/cosmos-db/performance-tips-java
这是我当前的 Java 设置,尽管我尝试了很多不同的设置:
//This piece of code is currently in a seperate thread. There is one thread per deviceId to query
documentClient = new DocumentClient(HOST, MASTER_KEY,
ConnectionPolicy.GetDefault(), ConsistencyLevel.Session);
FeedOptions options = new FeedOptions();
options.setEnableCrossPartitionQuery(true);
documentList = documentClient
.queryDocuments(getAlldataCollection().getSelfLink(), query, options)
.getQueryIterable().toList();
我相当确定 MongoDB 可以在几秒钟内查询数十万个文档,所以我非常确定我当前的设置有问题。
有什么建议吗?
我无法为您的问题提供明确的解决方案,但希望能为您提供想法,帮助您获得具有所需性能水平的解决方案。
NoSql 适合吗?
首先,为了摆脱 table,您确定您的场景适合 noSQL 吗?当主要场景处理精确数据(创建、select-by-id、update-by-id、delete-by-id)时,CosmosDB 大放异彩。是的,它确实可以进行有限的大规模操作和聚合,但查询数百万正在推动它。 SQL 另一方面 had 旨在处理大量数据,并且非常擅长进行聚合。
让我们假设这个设计决定是经过仔细权衡的,并且由于未提及的原因,没有SQL是最合适的。
硬数据调试
不要对本地 cosmosDB 模拟器进行性能测试。不。这显然不是真实的(考虑网络、存储 bandwidth/seek 次、系统影响),而只是模拟它。您可能会得到非常具有误导性的结果。 启动一个真实的测试实例。
调试查询性能问题的第一步是 启用 query-execution-metrics 并查看这 20 秒实际用在了哪里。
此外,加载 38000 个文档很可能永远不会成批到达,请检查实际对 cosmosDB 服务器进行了多少次连续查询。
此外,运行 分析器 并确保瓶颈确实在 CosmosDB 中。如果您正在进行许多连续调用并同时查询许多设备,那么客户端中也可能会发生很多事情,并且查询会在网络上飞来飞去。确保您没有在客户端(GC、Http 堆栈、内部锁定、connection/thread 池等)中受到限制。
Data/Query设计
减少查询数据
如果您已经知道 deviceid
,那么不要查询 38000 次以上 - 那只是压舱物。
减小模型对象大小
/* around 200 more key - value pairs */
那是一个巨大的物体。我会测试将它拆分成更小的对象是否会帮助 cosmosDB 花费更少的时间在内部加载和处理文档。例如:
{
"p_A2": "93095",
"p_A3": "303883",
"battery" : {
"current": "4294967.10000",
"gauge": "38.27700",
"voltage": "13.59400"
}
...
}
不确定 docDB 如何在内部存储文档(完整图表与子文档),但您可以测试它是否会产生影响。 2s 和 20s 的差异如此之大,以至于暗示它可能是相关的。
传感器阵列?
查询只查询第一个第一个测量集。阵列是必要的吗?您可以测试省略此级别是否会影响性能。
模型中的数据类型
battery_current
等将传感器测量数值存储为较长的字符串。如果它们总是数字,那么您可以将它们存储为数字,并减少服务器和客户端中的文档大小。客户端性能可能会受到更多影响(字符串 = 堆分配)。例如:"4294967.10000"
在客户端 (UTF-16) 中是 13 个字符 = 26B。
应用程序设计
您真的每次都需要那么多 38000 份或数百万份文件吗?考虑一下你是否可以通过一个子集..
如果这是为了数据移动,则考虑其他选项(数据工厂、更改提要处理)以增量传输测量值。如果这是按需应用程序需要,请考虑加载 更小的时间范围 (= 更少的文档)并对过去的时间范围 使用缓存 。如果可以,在缓存之前预先聚合结果。过去的传感器数据很可能不会改变。
一如既往,考虑投资回报率的业务案例。优化总是可能的,但有时调整业务需求而不是技术解决方案更有益。
正在运行时从文档中提取传感器数据。如此有效地解析和处理了一个字符串 blob。您需要支付提取传感器字段所需的资源消耗。
当您 运行 select * 查询只是返回整个 blob,因此不需要解析。
当 select 仅包含已编制索引的字段时。索引数据满足查询的可能性非常高。因此,无需访问文档数据。
我会尝试两件事。在索引策略中包含传感器路径。这将增加查询引擎仅通过访问索引结构就可以处理查询的可能性。
二、去掉order by,这样会进一步减少服务器端的处理。
我们已经在云中实现了 Azure CosmosDB(MongoDB 和 SQL API)数据库。通过java,我们想根据隐藏在MongoDB中的数据生成报告。我对读取查询的性能还不太满意,我想知道我当前的设置可以改进什么。
如前所述,我使用Java查询数据库。我使用 Microsoft Azure DocumentDB 库查询数据库:
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-documentdb</artifactId>
<version>1.16.2</version>
</dependency>
目前,我能够获得的最佳性能是在大约 20 秒内查询内存中的大约 38.000 个文档,配置了 50,000 RU/s(本地 cosmos 模拟器)。我真的很希望这个改进,因为我们可能很快就会查询数百万个文档。
我感觉我们存储数据的方式可能不是最优的。每个文件现在看起来如下:
{
"deviceid": "xxx",
"devicedata": {
"datetime": "2018-08-28T00:00:02.104Z",
"sensors": [
{
"p_A2": "93095",
"p_A3": "303883",
"p_batterycurrent": "4294967.10000",
"p_batterygauge": "38.27700",
"p_batteryvoltage": "13.59400",
** ... around 200 more key - value pairs ... **
}
]
},
"id": "aa5d3cf5-10fa-48dd-a0d2-a536284eddac",
"_rid": "PtEIANkbMQABAAAAAAAAAA==",
"_self": "dbs/PtEIAA==/colls/PtEIANkbMQA=/docs/PtEIANkbMQABAAAAAAAAAA==/",
"_etag": "\"00000000-0000-0000-4040-006a7f2501d4\"",
"_attachments": "attachments/",
"_ts": 1535619672
}
我们经常使用的查询如下所示:
SELECT c.deviceid,
c.devicedata.datetime,
c.devicedata.sensors[0].p_A2,
c.devicedata.sensors[0].p_A3,
c.devicedata.sensors[0].p_batterycurrent,
c.devicedata.sensors[0].s_humidity
FROM c
WHERE c.deviceid = 'xxx'
AND c.devicedata.datetime >= '2018-08-28T00:00:00.000Z'
AND c.devicedata.datetime < '2018-08-30T00:00:00.000Z'
order by c.devicedata.datetime desc
我根据 deviceId 减少了这些查询。因此,对于每个设备,我 运行 一个带有此查询的线程。这似乎比使用单个查询的单个线程快得多。
上面这样的查询大约需要 20 秒。
但是我注意到,如果我只查询 deviceid 和 devicedata.datetime,查询会在 2 秒内完成。从传感器列表中获取传感器数据似乎是一件非常困难的事情。如果我执行 select *(因此不过滤传感器数据),它也比我让 SQL API 过滤传感器时更快:大约 15 秒。
我的问题是,我可以做些什么来改进它?我的文档列表是否太长?有什么办法可以不同地设置它吗?传感器键值对不固定,可能因设备而异。
更多技术细节: 我有一个无限 collection,分区在 /deviceid 上。 我使用了 Azure 的标准索引策略(索引所有内容),并从中排除了传感器。
我已经尝试了这里描述的所有技巧: https://docs.microsoft.com/en-us/azure/cosmos-db/performance-tips-java
这是我当前的 Java 设置,尽管我尝试了很多不同的设置:
//This piece of code is currently in a seperate thread. There is one thread per deviceId to query
documentClient = new DocumentClient(HOST, MASTER_KEY,
ConnectionPolicy.GetDefault(), ConsistencyLevel.Session);
FeedOptions options = new FeedOptions();
options.setEnableCrossPartitionQuery(true);
documentList = documentClient
.queryDocuments(getAlldataCollection().getSelfLink(), query, options)
.getQueryIterable().toList();
我相当确定 MongoDB 可以在几秒钟内查询数十万个文档,所以我非常确定我当前的设置有问题。
有什么建议吗?
我无法为您的问题提供明确的解决方案,但希望能为您提供想法,帮助您获得具有所需性能水平的解决方案。
NoSql 适合吗?
首先,为了摆脱 table,您确定您的场景适合 noSQL 吗?当主要场景处理精确数据(创建、select-by-id、update-by-id、delete-by-id)时,CosmosDB 大放异彩。是的,它确实可以进行有限的大规模操作和聚合,但查询数百万正在推动它。 SQL 另一方面 had 旨在处理大量数据,并且非常擅长进行聚合。
让我们假设这个设计决定是经过仔细权衡的,并且由于未提及的原因,没有SQL是最合适的。
硬数据调试
不要对本地 cosmosDB 模拟器进行性能测试。不。这显然不是真实的(考虑网络、存储 bandwidth/seek 次、系统影响),而只是模拟它。您可能会得到非常具有误导性的结果。 启动一个真实的测试实例。
调试查询性能问题的第一步是 启用 query-execution-metrics 并查看这 20 秒实际用在了哪里。
此外,加载 38000 个文档很可能永远不会成批到达,请检查实际对 cosmosDB 服务器进行了多少次连续查询。
此外,运行 分析器 并确保瓶颈确实在 CosmosDB 中。如果您正在进行许多连续调用并同时查询许多设备,那么客户端中也可能会发生很多事情,并且查询会在网络上飞来飞去。确保您没有在客户端(GC、Http 堆栈、内部锁定、connection/thread 池等)中受到限制。
Data/Query设计
减少查询数据
如果您已经知道 deviceid
,那么不要查询 38000 次以上 - 那只是压舱物。
减小模型对象大小
/* around 200 more key - value pairs */
那是一个巨大的物体。我会测试将它拆分成更小的对象是否会帮助 cosmosDB 花费更少的时间在内部加载和处理文档。例如:
{
"p_A2": "93095",
"p_A3": "303883",
"battery" : {
"current": "4294967.10000",
"gauge": "38.27700",
"voltage": "13.59400"
}
...
}
不确定 docDB 如何在内部存储文档(完整图表与子文档),但您可以测试它是否会产生影响。 2s 和 20s 的差异如此之大,以至于暗示它可能是相关的。
传感器阵列?
查询只查询第一个第一个测量集。阵列是必要的吗?您可以测试省略此级别是否会影响性能。
模型中的数据类型
battery_current
等将传感器测量数值存储为较长的字符串。如果它们总是数字,那么您可以将它们存储为数字,并减少服务器和客户端中的文档大小。客户端性能可能会受到更多影响(字符串 = 堆分配)。例如:"4294967.10000"
在客户端 (UTF-16) 中是 13 个字符 = 26B。
应用程序设计
您真的每次都需要那么多 38000 份或数百万份文件吗?考虑一下你是否可以通过一个子集..
如果这是为了数据移动,则考虑其他选项(数据工厂、更改提要处理)以增量传输测量值。如果这是按需应用程序需要,请考虑加载 更小的时间范围 (= 更少的文档)并对过去的时间范围 使用缓存 。如果可以,在缓存之前预先聚合结果。过去的传感器数据很可能不会改变。
一如既往,考虑投资回报率的业务案例。优化总是可能的,但有时调整业务需求而不是技术解决方案更有益。
正在运行时从文档中提取传感器数据。如此有效地解析和处理了一个字符串 blob。您需要支付提取传感器字段所需的资源消耗。
当您 运行 select * 查询只是返回整个 blob,因此不需要解析。
当 select 仅包含已编制索引的字段时。索引数据满足查询的可能性非常高。因此,无需访问文档数据。
我会尝试两件事。在索引策略中包含传感器路径。这将增加查询引擎仅通过访问索引结构就可以处理查询的可能性。
二、去掉order by,这样会进一步减少服务器端的处理。