你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
- Java SDK v4
- 异步 Java SDK v2
- 同步 Java SDK v2
- .NET SDK v3
- .NET SDK v2
- Python SDK
重要
这不是最新的 Azure Cosmos DB Java SDK! 应将项目升级到 Azure Cosmos DB Java SDK v4 ,然后阅读 Azure Cosmos DB Java SDK v4 性能提示指南。 按照 迁移到 Azure Cosmos DB Java SDK v4 指南和 Reactor vs RxJava 指南中的说明进行升级。
本文中的性能提示仅适用于 Azure Cosmos DB Async Java SDK v2。 有关详细信息,请参阅 Azure Cosmos DB Async Java SDK v2 发行说明、 Maven 存储库和 Azure Cosmos DB Async Java SDK v2 故障排除指南 。
重要
2024 年 8 月 31 日,Azure Cosmos DB Async Java SDK v2.x 将停用;使用 SDK 的 SDK 和所有应用程序 将继续正常运行;Azure Cosmos DB 将停止为此 SDK 提供进一步的维护和支持。 我们建议按照上述说明迁移到 Azure Cosmos DB Java SDK v4。
Azure Cosmos DB 是一个快速、弹性的分布式数据库,可以在提供延迟与吞吐量保证的情况下无缝缩放。 凭借 Azure Cosmos DB,无需对体系结构进行重大更改或编写复杂的代码即可缩放数据库。 扩展和缩减操作就像执行单个 API 调用或 SDK 方法调用一样简单。 但是,由于 Azure Cosmos DB 是通过网络调用访问的,因此可以通过客户端优化在使用 Azure Cosmos DB Async Java SDK v2 时实现最佳性能。
因此,如果询问“如何提高数据库性能?”,请考虑以下选项:
网络
连接模式:使用直接模式
客户端如何连接到 Azure Cosmos DB 对性能有重要影响,尤其是在客户端延迟方面。 ConnectionMode 是可用于配置客户端 ConnectionPolicy 的关键配置设置。 对于 Azure Cosmos DB 异步 Java SDK v2,这两种可用的 ConnectionModes 为:
所有 SDK 平台上都支持网关模式,并且默认为已配置选项。 如果应用程序在具有严格防火墙限制的企业网络中运行,则网关模式是最佳选择,因为它使用标准 HTTPS 端口和单个终结点。 但是,性能权衡在于,每次将数据读取或写入 Azure Azure Cosmos DB 时,网关模式都会涉及额外的网络跃点。 因此,由于网络跃点较少,直接模式可提供更好的性能。
在构造 DocumentClient 实例时,使用 ConnectionPolicy 参数配置 ConnectionMode。
异步 Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
public ConnectionPolicy getConnectionPolicy() {
ConnectionPolicy policy = new ConnectionPolicy();
policy.setConnectionMode(ConnectionMode.Direct);
policy.setMaxPoolSize(1000);
return policy;
}
ConnectionPolicy connectionPolicy = new ConnectionPolicy();
DocumentClient client = new DocumentClient(HOST, MASTER_KEY, connectionPolicy, null);
将客户端并置在同一 Azure 区域内以提高性能
如果可能,请将任何调用 Azure Cosmos DB 的应用程序放在与 Azure Cosmos DB 数据库所在的相同区域中。 通过大致的比较发现,在同一区域中对 Azure Cosmos DB 的调用可在 1-2 毫秒内完成,而美国西海岸和美国东海岸之间的延迟则 > 50 毫秒。 根据请求采用的路由,各项请求从客户端传递到 Azure 数据中心边界时的此类延迟可能有所不同。 为了实现最低的延迟,需要确保应用程序调用位于与预配的 Azure Cosmos DB 终结点相同的 Azure 区域中。 有关可用区域的列表,请参阅 Azure Regions(Azure 区域)。
SDK 用法
安装最新的 SDK
Azure Cosmos DB SDK 正在不断改进以提供最佳性能。 请参阅 Azure Cosmos DB Async Java SDK v2 发行说明 页,以确定最新的 SDK 并查看改进。
在应用程序生存期内使用单一实例 Azure Cosmos DB 客户端
每个 AsyncDocumentClient 实例都是线程安全的,并执行高效的连接管理和地址缓存。 若要通过 AsyncDocumentClient 实现高效的连接管理和更好的性能,建议在应用程序的生存期内使用 AsyncDocumentClient per AppDomain 的单个实例。
优化 ConnectionPolicy
默认情况下,使用 Azure Cosmos DB 异步 Java SDK v2 时,会通过 TCP 发出直接模式 Azure Cosmos DB 请求。 在内部,SDK 使用特殊的直接模式体系结构来动态管理网络资源并获得最佳性能。
在 Azure Cosmos DB 异步 Java SDK v2 中,直接模式是使用大多数工作负荷提高数据库性能的最佳选择。
- 直接模式概述
直接模式下使用的客户端体系结构可实现对 Azure Cosmos DB 副本的可预测网络利用率和多路复用访问。 上图显示了直接模式如何将客户端请求路由到 Azure Cosmos DB 后端中的副本。 直接模式体系结构在每个 DB 副本的客户端上最多分配 10 个通道 。 通道是请求缓冲区之前的 TCP 连接,该缓冲区深度为 30 个请求。 属于副本的通道由副本 的服务终结点根据需要动态分配。 当用户在直接模式下发出请求时, TransportClient 会根据分区键将请求路由到适当的服务终结点。 请求队列在服务终结点之前缓冲请求。
Direct 模式的连接策略配置选项
第一步是使用以下建议的配置设置。 如果遇到有关此特定主题的问题,请联系 Azure Cosmos DB 团队 。
如果使用 Azure Cosmos DB 作为引用数据库(即,数据库用于许多点读取作和少量写入作),则可以接受将 idleEndpointTimeout 设置为 0(即无超时)。
配置选项 违约 bufferPageSize 8192 connectionTimeout 连接超时 “PT1M” idleChannelTimeout “PT0S” idleEndpointTimeout “PT1M10S” maxBufferCapacity 8388608 最大通道数每终端 10 maxRequestsPerChannel 30 接收挂起检测时间 “PT1M5S” requestExpiryInterval “PT5S” requestTimeout “PT1M” 请求定时器分辨率 “PT0.5S” sendHangDetectionTime “PT10S” shutdownTimeout (关闭超时时间) “PT15S”
直接模式的编程提示
查看 Azure Cosmos DB 异步 Java SDK v2 故障排除 文章作为解决任何 SDK 问题的基线。
使用直接模式时的一些重要编程提示:
在应用程序中使用多线程进行高效的 TCP 数据传输 - 发出请求后,应用程序应订阅在另一个线程上接收数据。 不这样做会强制发生意外的“半双工操作”,后续请求将被阻止,等待前一个请求的回复。
在专用线程上执行计算密集型工作负荷 - 出于与上一提示类似的原因,复杂数据处理等作最好放在单独的线程中。 从另一个数据存储拉取数据的请求(例如,如果线程同时利用 Azure Cosmos DB 和 Spark 数据存储),可能会遇到延迟增加的请求,建议生成等待来自其他数据存储的响应的其他线程。
- Azure Cosmos DB 异步 Java SDK v2 中的基础网络 IO 由 Netty 管理。 请参阅这些 提示,了解如何避免阻止 Netty IO 线程的编码模式。
数据建模 - Azure Cosmos DB SLA 假定文档大小小于 1 KB。 优化数据模型和编程以偏向较小的文档大小通常会导致延迟降低。 如果需要存储和检索大于 1 KB 的文档,建议的方法是让文档链接到 Azure Blob 存储中的数据。
优化已分区集合的并行查询
Azure Cosmos DB 异步 Java SDK v2 支持并行查询,使你能够并行查询分区集合。 有关详细信息,请参阅与使用 SDK 相关的 代码示例 。 并行查询旨在与串行查询相比,改善查询的延迟和吞吐量。
优化 setMaxDegreeOfParallelism:
并行查询的方式是并行查询多个分区。 但是,对于查询,会按顺序提取单个分区集合中的数据。 因此,使用 setMaxDegreeOfParallelism 设置能够达到最高性能查询的分区数,前提是所有其他系统条件保持不变。 如果不知道分区数,则可以使用 setMaxDegreeOfParallelism 设置高数字,并且系统选择最小(分区数、用户提供的输入)作为最大并行度。
请务必注意,如果数据在查询的所有分区之间均匀分布,并行查询将产生最佳优势。 如果分区集合的方式导致查询返回的所有或大部分数据集中在少数几个分区(最坏情况下为单个分区),那么查询的性能将受到这些分区的制约。
优化 setMaxBufferedItemCount:
并行查询旨在预提取结果,而客户端正在处理当前批结果。 预提取有助于改善查询的总体延迟。 setMaxBufferedItemCount 限制预提取的结果数。 将 setMaxBufferedItemCount 设置为返回的结果的预期数量(或更高的数字),使查询能够从预提取中获得最大好处。
预提取的工作方式与 MaxDegreeOfParallelism 无关,并且所有分区的数据共用一个缓冲区。
在 getRetryAfterInMilliseconds 的间隔时间内实现退避
在性能测试期间,应增加负载,直到小部分请求被限制。 如果受到限制,客户端应用程序应为服务器指定的重试间隔退避。 恪守退避策略可确保在重试之间将等待时间最小化。
增大客户端工作负荷
如果要在高吞吐量级别(>50,000 RU/秒)进行测试,客户端应用程序可能会成为瓶颈,因为计算机在 CPU 或网络利用率上达到上限。 如果达到此阶段,可以跨多个服务器横向扩展客户端应用程序,以进一步推动 Azure Cosmos DB 帐户的发展。
使用基于名称的寻址
使用基于名称的寻址,其中链接格式为
dbs/MyDatabaseId/colls/MyCollectionId/docs/MyDocumentId,而不是采用格式dbs/<database_rid>/colls/<collection_rid>/docs/<document_rid>的自链接(_self),这样可以避免检索构建链接所需的所有资源的 ResourceId。 此外,由于这些资源重新创建(可能使用相同的名称),因此缓存它们可能无济于事。优化查询/读取源的页面大小以提高性能
使用批量文档读取功能(例如 readDocuments)或执行 SQL 查询时,如果结果集过大,则结果将以分段形式返回。 默认情况下,结果以 100 个项目或 1 MB 为一组返回,具体取决于先达到哪个限制。
若要减少检索所有适用结果所需的网络往返次数,可以使用 x-ms-max-item-count 请求标头将页面大小增加到最多 1000。 如果需要仅显示几个结果,例如,如果用户界面或应用程序 API 一次只返回 10 个结果,则还可以将页面大小减少到 10,以减少读取和查询使用的吞吐量。
还可以使用 setMaxItemCount 方法设置页面大小。
使用合适的调度器(避免占用事件循环 IO Netty 线程)
Azure Cosmos DB 异步 Java SDK v2 使用 netty 进行非阻塞IO。 SDK 使用固定数量的 IO netty 事件循环线程(数量与计算机提供的 CPU 核心数相同)来执行 IO 操作。 API 返回的 Observable 在一个共享 IO 事件循环 netty 线程上发出结果。 因此,切勿阻塞共享的 IO 事件循环 netty 线程。 在 IO 事件循环的 Netty 线程上执行 CPU 密集型工作或阻塞操作可能会导致死锁或显著降低 SDK 吞吐量。
例如,以下代码对事件循环 IO netty 线程执行 CPU 密集型工作:
异步 Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
Observable<ResourceResponse<Document>> createDocObs = asyncDocumentClient.createDocument( collectionLink, document, null, true); createDocObs.subscribe( resourceResponse -> { //this is executed on eventloop IO netty thread. //the eventloop thread is shared and is meant to return back quickly. // // DON'T do this on eventloop IO netty thread. veryCpuIntensiveWork(); });接收到结果后,如果想要对该结果执行 CPU 密集型工作,应该避免在事件循环 IO Netty 线程上进行操作。 你也可以提供自己的调度器,以便为执行工作提供自己的线程。
Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
import rx.schedulers; Observable<ResourceResponse<Document>> createDocObs = asyncDocumentClient.createDocument( collectionLink, document, null, true); createDocObs.subscribeOn(Schedulers.computation()) subscribe( resourceResponse -> { // this is executed on threads provided by Scheduler.computation() // Schedulers.computation() should be used only when: // 1. The work is cpu intensive // 2. You are not doing blocking IO, thread sleep, etc. in this thread against other resources. veryCpuIntensiveWork(); });根据工作类型,应为工作使用适当的现有 RxJava 计划程序。 请阅读
Schedulers。有关详细信息,请查看 Azure Cosmos DB Async Java SDK v2 的 GitHub 页 。
禁用 netty 的日志记录
Netty 库的日志记录冗长,需要关闭(仅在配置中禁止可能不够),以避免额外的 CPU 成本。 如果不处于调试模式,请彻底禁用 Netty 的日志记录。 因此,如果您使用 log4j 来减少由 netty 引起的额外 CPU 成本,请将以下行添加到代码基中:
org.apache.log4j.Logger.getLogger("io.netty").setLevel(org.apache.log4j.Level.OFF);OS 打开文件资源限制
某些 Linux 系统(例如 Red Hat)对打开的文件数和连接总数施加了上限。 运行以下命令以查看当前限制:
ulimit -a打开文件数量(nofile)需要足够大,以便能够容纳您配置的连接池大小和操作系统打开的其他文件。 可以修改此参数,以增大连接池大小。
打开 limits.conf 文件:
vim /etc/security/limits.conf添加/修改以下行:
* - nofile 100000
索引策略
从索引中排除未使用的路径以加快写入速度
借助 Azure Cosmos DB 的索引策略,可以通过使用索引路径(setIncludedPaths 和 setExcludedPaths)来指定要在索引中包括或排除的文档路径。 在事先知道查询模式的方案中,使用索引路径可改善写入性能并降低索引存储空间,因为索引成本与索引的唯一路径数目直接相关。 例如,以下代码演示如何使用“*”通配符从索引中排除文档(也称为子树)的整个部分。
异步 Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
Index numberIndex = Index.Range(DataType.Number); numberIndex.set("precision", -1); indexes.add(numberIndex); includedPath.setIndexes(indexes); includedPaths.add(includedPath); indexingPolicy.setIncludedPaths(includedPaths); collectionDefinition.setIndexingPolicy(indexingPolicy);有关索引的详细信息,请参阅 Azure Cosmos DB 索引策略。
吞吐量
测量和调整以减少每秒请求单位的使用量
Azure Cosmos DB 提供一组丰富的数据库操作,包括 UDF 的关系和层次查询、存储过程和触发 – 所有都在数据库集合的文档上操作。 与这些操作关联的成本取决于完成操作所需的 CPU、IO 和内存。 与考虑和管理硬件资源不同的是,可以考虑将请求单位 (RU) 作为所需资源的单个措施,以执行各种数据库操作和服务应用程序请求。
吞吐量是基于为每个容器设置的请求单位数量预配的。 请求单位消耗以每秒速率评估。 如果应用程序的速率超过了为其容器预配的请求单位速率,则会受到限制,直到该速率降到容器的预配级别以下。 如果应用程序需要较高级别的吞吐量,可以通过预配更多请求单位来增加吞吐量。
查询的复杂性会影响操作使用的请求单位数量。 谓词数、谓词性质、UDF 数目和源数据集的大小都会影响查询操作的成本。
若要测量任何操作(创建、更新或删除)的开销,请检查 x-ms-request-charge 标头来测量这些操作占用的请求单位数。 也可以在 ResourceResponse<T> 或 FeedResponse<T> 中找到等效的 RequestCharge 属性。
异步 Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
ResourceResponse<Document> response = asyncClient.createDocument(collectionLink, documentDefinition, null, false).toBlocking.single(); response.getRequestCharge();在此标头中返回的请求费用是您预设吞吐量的一小部分。 例如,如果预配了 2000 RU/秒,并且上述查询返回 1,000 1 KB 文档,则作成本为 1000。 因此在一秒内,服务器在对后续请求进行速率限制之前,只接受两个此类请求。 有关详细信息,请参阅请求单位和请求单位计算器。
处理速率限制/请求速率太大
客户端尝试超过帐户保留的吞吐量时,服务器的性能不会降低,并且不会使用超过保留级别的吞吐量容量。 服务器将抢先结束 RequestRateTooLarge(HTTP 状态代码 429)的请求并返回 x-ms-retry-after-ms 标头,该标头指示重新尝试请求前用户必须等待的时间量(以毫秒为单位)。
HTTP Status 429, Status Line: RequestRateTooLarge x-ms-retry-after-ms :100SDK 全部都会隐式捕获此响应,并遵循服务器指定的 retry-after 标头,并重试请求。 除非多个客户端同时访问帐户,否则下次重试就会成功。
如果有多个客户端持续以高于请求速率的速率运行,那么客户端内部默认设置的重试计数为 9 可能不足。在这种情况下,客户端会向应用程序抛出一个状态代码为 429 的 DocumentClientException。 可以使用 ConnectionPolicy 实例上的 setRetryOptions 更改默认重试计数。 默认情况下,如果请求继续运行高于请求速率,则状态代码为 429 的 DocumentClientException 在累积等待时间 30 秒后返回。 即使当前的重试计数小于最大重试计数(默认值 9 或用户定义的值),也会发生这种情况。
尽管自动重试行为有助于改善大多数应用程序的复原能力和可用性,但是在执行性能基准测试时可能会造成冲突(尤其是在测量延迟时)。 如果实验触发服务器节流并导致客户端 SDK 自动重试,则客户端观测到的延迟会剧增。 为了避免性能实验期间出现延迟高峰,应评估每个操作返回的费用,并确保请求在低于预定请求速率的水平下运行。 有关详细信息,请参阅请求单位。
针对小型文档进行设计以提高吞吐量
给定操作的请求费用(请求处理成本)与文档大小直接相关。 大型文档的操作成本高于小型文档的操作成本。
后续步骤
若要深入了解如何设计应用程序以实现缩放和高性能,请参阅 Azure Cosmos DB 中的分区和缩放。