在 Node.js 中使用 MongoDB,mongoose 是绕不开的库。它把官方驱动的回调风格封装成更易用的 Schema 和 Model,还提供了校验、中间件、填充等能力。然而很多人在初次接入时只写了一句 mongoose.connect(uri) 就完事,等到请求量上来之后发现连接经常挂起、查询变慢,甚至进程无响应。其实 Mongoose 底层走的是 MongoDB Node.js 官方驱动,连接参数非常丰富,默认值不一定适合所有环境。比如 serverSelectionTimeoutMS 默认 30 秒,在服务器不可达时会让应用阻塞很久;连接池大小默认只有 5,在高并发场景下会形成瓶颈。

下面就把常用配置拆开,从连接字符串、连接池、认证与 TLS 以及排错思路几个方向详细说明。内容会尽量贴近实际生产环境,避免只讲理论。
连接字符串与基础选项怎么设置
Mongoose 的连接字符串遵循 MongoDB 官方 URI 格式,以 mongodb:// 开头,或者使用 DNS 种子列表的 mongodb+srv:// 形式。最简单的本地连接可以写成 mongodb://127.0.0.1:27017/mydb,如果数据库有认证,则需要把用户名和密码放进 URI 中,例如 mongodb://user:password@127.0.0.1:27017/mydb。如果密码中含有特殊字符,必须使用 encodeURIComponent 进行转义,否则字符串会被截断或解析错误。
一个生产环境常用的连接字符串通常会带上 authSource 参数,用来指定认证数据库。例如很多云数据库要求认证库是 admin,而业务库是单独的名字,这时 URI 就要写成 mongodb://user:password@host:27017/mydb?authSource=admin。如果使用的是副本集,还可以通过 replicaSet 参数指定副本集名称,这样驱动在发生主从切换时才能自动发现新的主节点。readPreference 参数也很有用,比如设置 readPreference=secondaryPreferred 可以让读请求优先走从节点,降低主库压力。
连接选项方面,Mongoose 5 时代大家习惯加 useNewUrlParser 和 useUnifiedTopology,但从 Mongoose 6 开始这两个选项已经成为默认行为,不再需要显式传递。现在更值得关注的是超时相关参数。serverSelectionTimeoutMS 控制驱动在多长时间内找不到可用服务器就报错,默认是 30000 毫秒,对于快速失败的场景太长了,可以改成 5000。socketTimeoutMS 则是单次 socket 读写的超时,默认 0 表示不超时,但为了防止连接僵死,建议设置成 45000 左右。另一个 connectTimeoutMS 控制建立 TCP 连接的超时,默认 10000,一般不用改,除非网络特别差。
下面这段代码演示了如何配置一个适合多数 Web 应用的基础连接:
const mongoose = require('mongoose');
const uri = 'mongodb://user:password@127.0.0.1:27017/mydb?authSource=admin';
mongoose.connect(uri, {
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
connectTimeoutMS: 10000,
maxPoolSize: 20,
minPoolSize: 5
}).then(() => {
console.log('MongoDB 连接成功');
}).catch(err => {
console.error('MongoDB 连接失败', err);
});
这段代码里的 then 和 catch 是 Mongoose 6 之后 connect 返回 Promise 的常规写法。连接成功后就可以放心使用 Model 进行查询了。不过这里设置的 maxPoolSize 和 minPoolSize 是连接池参数,接下来单独展开讲。
连接池参数怎么调优
连接池是官方驱动内置的能力,目的是复用已经建立好的 TCP 连接,避免每次查询都重新握手。Mongoose 通过选项把连接池参数透传给底层驱动,其中最核心的是 maxPoolSize。这个值决定了一个 Mongoose 实例最多能同时保持多少条数据库连接。如果应用并发很高,但连接池只有默认的 5,那么请求会排队等待连接释放,表现为接口响应时间突然增大,甚至触发上游超时。
调整连接池大小时,不能一味求大。每个连接都会占用数据库服务端的内存和文件描述符,如果实例数量很多、每个实例又设置了很大的 maxPoolSize,数据库端可能先撑不住。一般经验是:单个 Node.js 进程的 maxPoolSize 设置为 10 到 50 之间,具体看业务 QPS 和单条查询耗时。可以通过监控数据库连接数来反推,如果连接数常年接近上限,说明池子小了;如果连接数远小于 maxPoolSize,说明设置过大,可以适当下调。
minPoolSize 则用于在应用启动时预先创建一定数量的连接,避免冷启动时第一次查询建立连接的额外延迟。对于流量很平稳的服务,设置 minPoolSize 等于或略小于 maxPoolSize 是有好处的。maxIdleTimeMS 用来控制空闲连接多久之后被回收,默认是 0 表示不回收,但很多云数据库会主动断开空闲连接,因此建议设置一个比服务端超时略小的值,比如 30000 到 60000 毫秒。需要注意,老版本驱动中的 waitQueueTimeoutMS 在 4.x 及以上版本已经被移除,连接池满时不会再排队等待,而是直接尝试创建新连接,超过 maxPoolSize 后就会抛错,所以不要再设置这个废弃选项。
下面给出一个连接池调优的配置示例,并加上事件监听方便观察连接状态:
const mongoose = require('mongoose');
mongoose.connection.on('connected', () => {
console.log('Mongoose 已连接');
});
mongoose.connection.on('disconnected', () => {
console.log('Mongoose 连接断开');
});
mongoose.connection.on('error', (err) => {
console.error('Mongoose 连接错误', err);
});
mongoose.connect('mongodb://127.0.0.1:27017/mydb', {
maxPoolSize: 30,
minPoolSize: 10,
maxIdleTimeMS: 45000,
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 60000
});
通过监听 connected、disconnected 和 error 事件,可以在连接出现异常时及时告警或尝试重连。Mongoose 本身有自动重连机制,但如果连接池参数设置不合理,自动重连后可能仍然无法承载流量,所以配置与监控要配合起来做。
认证、TLS 与常见错误排查
生产环境几乎都会开启认证和 TLS。认证主要通过 URI 中的 authSource 和用户名密码完成,但如果数据库使用了 X.509 证书认证或 LDAP 代理,还需要调整 authMechanism。例如使用 SCRAM-SHA-256 时默认即可,使用 MONGODB-X509 时则需要指定 authMechanism=MONGODB-X509,并且客户端证书也要通过选项传入。
TLS 配置通常写在 URI 的 query 参数里,比如 tls=true 表示启用 TLS,tlsAllowInvalidCertificates=false 表示不允许无效证书。如果数据库使用的是自签名证书或内部 CA 签发的证书,需要把 CA 证书路径通过 tlsCAFile 传给驱动,同时设置 tlsCertificateKeyFile 指向客户端证书。下面是一个带认证和 TLS 的连接示例:
const mongoose = require('mongoose');
const uri = 'mongodb://appuser:secret@db.internal:27017/mydb?authSource=admin&tls=true';
mongoose.connect(uri, {
tlsCAFile: '/etc/certs/ca.pem',
tlsCertificateKeyFile: '/etc/certs/client.pem',
tlsAllowInvalidCertificates: false,
authMechanism: 'SCRAM-SHA-256',
maxPoolSize: 20
}).catch(err => {
console.error('TLS 连接失败', err.message);
});
如果不确定证书路径是否正确,可以先在命令行用 mongosh 测试连接,确认网络、认证和 TLS 都能通过后再迁移到应用代码中。一个小技巧是临时把 tlsAllowInvalidCertificates 设为 true 来排查是不是证书校验问题,但生产环境必须改回 false。
常见的错误类型中,MongooseServerSelectionError 一般表示在 serverSelectionTimeoutMS 时间内没有找到可用服务器,原因可能是 IP 白名单没加、端口不通、副本集名称写错或者 DNS 解析失败。Authentication failed 则多半是 authSource 不对或者密码错误,也有可能是用户没有目标库的权限。Topology destroyed 错误通常发生在应用关闭时没有正确断开连接,或者连接过程中实例被销毁。针对关闭场景,建议在进程退出前调用 mongoose.disconnect(),并等待连接关闭完成后再结束进程。
总结一下,Mongoose 驱动配置并不复杂,关键是把连接字符串、超时参数、连接池大小、认证与 TLS 这几块都考虑到位。配置完成后最好配合连接事件做日志和监控,这样才能在问题刚出现时及时发现并调整。与其等到线上超时再手忙脚乱,不如在部署前就把这些参数按环境分开管理。

