Docs 菜单
Docs 主页
/ / /
Rust 驱动程序
/

性能考虑因素

在此页面上

  • Overview
  • 客户端生命周期
  • 连接池
  • 配置最大池大小
  • 配置并发连接选项
  • 配置最大空闲时间
  • 并行度
  • 运行时
  • 更多信息
  • API 文档

在本指南中,您可以了解如何优化 Rust 驱动程序的性能。 要连接到 MongoDB,您必须创建一个 Client实例。 Client实例会自动处理连接的大多数方面,例如发现服务器拓扑结构、监控连接以及维护内部连接池。本指南介绍了配置和使用Client实例的最佳实践。

本指南包括以下部分:

  • 客户端生命周期描述了创建和管理Client实例的最佳实践

  • 连接池描述了连接池在驱动程序中的工作原理

  • 并行性提供了用于运行并行异步任务的示例代码

  • 运行时描述了如何使用tokioasync_std crate 的功能来管理运行时

  • 附加信息提供了本指南中提到的类型和方法的资源和 API 文档链接

我们建议您在会话和操作中重复使用客户端。 您可以使用同一Client实例来执行多个任务,因为Client类型可以安全地由多个线程并发使用。 为每个请求创建新的Client实例会导致性能下降。

以下代码创建一个接受指向现有Client实例的指针的方法,该实例允许您使用同一客户端执行多个请求:

// ... Create a client earlier in your code
async fn make_request(client: &Client) -> Result<(), Box<dyn Error>> {
// Use the client to perform operations
Ok(())
}

对于 MongoDB 拓扑结构中的每个服务器,每个 Client 实例都有一个内置连接池。连接池按需打开套接字以支持应用程序中对 MongoDB 的并发请求。

Client的默认配置适用于大多数应用程序。 以下代码展示了如何使用默认连接设置创建客户端:

let client = Client::with_uri_str("<connection string>").await?;

或者,您可以调整连接池以最好地满足应用程序的需求并优化性能。 有关如何自定义连接设置的更多信息,请参阅本指南的以下小节:

提示

要了解有关配置连接池的更多信息,请参阅 MongoDB Server手册中的 调整连接池设置 。

每个连接池的最大大小由max_pool_size选项设置,默认为10 。 如果正在使用的服务器连接数达到max_pool_size值,则对该服务器的下一个请求将等待,直到出现可用连接。

除了支持应用程序请求所需的套接字之外,每个Client实例还为 MongoDB 拓扑结构中的每个服务器再打开两个套接字,用于监控服务器的状态。 例如,连接到三节点副本集的客户端会打开六个监控套接字。 如果应用程序使用max_pool_size的默认设置,并且仅查询主(默认)节点,则连接池中的总连接数最多为 16。如果应用程序使用读取偏好来查询从节点,则这些连接池会增长,总连接数可为 36。

要在一个进程中支持大量并发 MongoDB 请求,可以增加max_pool_size选项的值。 以下代码演示了如何在实例化Client时为max_pool_size指定值:

let mut client_options = ClientOptions::parse("<connection string>").await?;
client_options.max_pool_size = Some(20);
let client = Client::with_options(client_options)?;

连接池的速率受到限制。 max_connecting选项决定连接池在任何时候可以并行创建的连接数量。 例如,如果max_connecting的值为默认值2 ,则仅当出现以下情况之一时,尝试同时签出连接的第三个请求才会成功:

  • 连接池已完成连接创建,并且池中的连接数小于或等于max_pool_size的值。

  • 现有连接会重新检入池中。

  • 由于对连接创建的速率限制,驱动程序重用现有连接的能力得到提高。

您可以使用min_pool_size选项设置每个服务器的最小并发连接数,默认为0 。 驱动程序使用这个数量的套接字初始化连接池。 如果套接字已关闭,并且正在使用和空闲的套接字总数低于最小值,则连接池将打开更多套接字,直到达到最小值。

以下代码在实例化Client时设置max_connectingmin_pool_size选项:

let mut client_options = ClientOptions::parse("<connection string>").await?;
client_options.max_connecting = Some(3);
client_options.min_pool_size = Some(1);
let client = Client::with_options(client_options)?;

您可以通过设置 max_idle_time 选项来设置一个连接在连接池中保持空闲状态的最大时间量。一旦连接持续处于空闲状态的时间达到 max_idle_time 中指定的时间,连接池就会删除并替换该连接。此选项默认为 0,即无限制。

在应用程序中的任何点调用Client::shutdown()方法时,驱动程序都会关闭所有空闲套接字,并关闭所有正在使用的套接字,因为它们会返回到池中。调用Client::shutdown()仅关闭非活动套接字,因此您无法使用此方法中断或终止任何正在进行的操作。 仅当进程完成时,驱动程序才会关闭这些套接字。

实例化Client时,以下代码将max_idle_time选项的值设置为90秒:

let mut client_options = ClientOptions::parse("<connection string>").await?;
client_options.max_idle_time = Some(Duration::new(90, 0));
let client = Client::with_options(client_options)?;

如果可以运行并行数据操作,则可以通过运行异步并发任务来优化性能。 以下代码使用tokio::task模块中的spawn()方法创建单独的并发任务来执行插入操作:

let client = Client::with_uri_str("<connection string>").await?;
let data = doc! { "title": "1984", "author": "George Orwell" };
for i in 0..5 {
let client_ref = client.clone();
let data_ref = data.clone();
task::spawn(async move {
let collection = client_ref
.database("items")
.collection::<Document>(&format!("coll{}", i));
collection.insert_one(data_ref).await
});
}

Client实例绑定到您在其中创建它的tokioasync-std运行时的实例。 如果使用Client实例在不同的运行时上执行操作,则可能会遇到意外行为或故障。

如果使用tokioasync_std包中的test助手宏来测试您的应用程序,您可能会意外在与预期不同的运行时运行时运行操作。 这是因为这些辅助宏会为每个测试创建一个新的运行时。 但是,您可以使用以下策略之一来避免此问题:

  • 将运行时附加到Client实例,而不使用test辅助宏。

  • 为每个async测试创建一个新的Client实例。

此示例遵循第一个策略,并创建一个仅用于测试的全局运行时。 在以下代码中, test_list_dbs()方法使用手动连接到此运行时的客户端来列出部署中的数据库:

use tokio::runtime::Runtime;
use once_cell::sync::Lazy;
static CLIENT_RUNTIME: Lazy<(Client, Runtime)> = Lazy::new(|| {
let rt = Runtime::new().unwrap();
let client = rt.block_on(async {
Client::with_uri_str("<connection string>").await.unwrap()
});
(client, rt)
});
#[test]
fn test_list_dbs() -> Result<(), Box<dyn Error>> {
let (client, rt) = &*CLIENT_RUNTIME;
rt.block_on(async {
client.list_database_names().await
})?;
Ok(())
}

在实施第二种策略时,以下代码为使用tokio::test的每次测试运行创建一个新的实例Client,确保运行时之间不会出现意外的交互:

#[tokio::test]
async fn test_list_dbs() -> Result<(), Box<dyn Error>> {
let client = Client::with_uri_str("<connection string>").await?;
client.list_database_names().await?;
Ok(())
}

要学习;了解有关连接到MongoDB的更多信息,请参阅连接指南。

要了解有关 Rust 驱动程序可用运行时的更多信息,请参阅异步和同步 API 指南。

后退

数据库命令