最近在一个已上线的项目中发现一个问题, 该项目的技术栈是:node+express+mongo+mongoose;
该项目每天的高峰期大概在 早8 -10 晚6- 7(晚上不是很厉害) 每天在高峰期的时候接口响应就很慢 ; 后来在代码里使用时间戳打印了一下数据库执行语句消耗时间:
//下单入口
router.post("/oderVipList", async (req, res) => {
//判断当前是否连接数据库,没有连接 · 则连接相应的数据库
let db = await dbData.getDataBaseConnection(req.hostname)
let vipCodeSM = db.model('vipCode', vipCodeSchema);
//接收前端传参
let { userIphone, vip, store, foodlist, method } = req.body
//记录 执行数据查询 前的时间戳
let startTimeC = new Date().valueOf()
//执行数据查询 , 只返回code字段
let vipIsResult = await vipCodeSM.findOne({vip:vip},{code:1})
//记录 执行数据查询 后的时间戳
let ynk = new Date().valueOf()
//查询后的时间戳 减去 查询前的时间戳 得到 执行数据查询的毫秒数
let ynkynk = ynk -startTimeC
//打印消耗时间 后面的chaTime值可不用看
console.log(vip+`查询兑换码“状态”字段消耗时间:${ynkynk},总下单消耗毫秒数${chaTime}`)
打印的结果: (因为截图的时间是晚上6点多,流量并比不了早上,早上的打印时间可高达6-8秒,并且连续一个多小时没有低于的)
这样看打印的 findOne 查询时间 使用的时间多少都有 (注意:因为我截图的时间是下午6点30左右,这时间点流量偏多,不但不是太高,早上的8-9点是特别高的,其他时间这里findOne时间都是 3- 4毫秒)
我起初从这里看认为是 这2000-3000毫秒的时候都是浪费在了数据库查询了,但是直到我 开启了 mongodb的慢查询,设置了slowms:300,并查询数据库执行超过300毫秒的,结果显示一条没有。
从这里我判断 这findOne的2000-3000豪秒时间,应该不是全消耗在数据操作上了。就考虑数据库连接池问题。
mongoose.set("useCreateIndex", true);
connections[hostq] = mongoose.createConnection(hostq, {
useNewUrlParser: true,
useUnifiedTopology: true,
poolSize: 50 //查资料是通过poolSize来配置连接池的
});
这样配置了poolSize的值,我看资料mongoose是有默认连接池是5, 我后来又给加到50 ,但还是不解决问题,又给加到150,再加到300,还是无济于事!
再通过查资料看到可检查到MongoDB的当前连接数:结果
看到 current当前连接数是39, available闲置连接:5万多。(不是很了解这个数据,具体知道什么意思)
这样看的话并没有发现我设置连接池poolSize启到了什么作用,这里不是很懂。
现在的问题是:
1.在高并发的情况下时间一个数据单数据查询时间通过我打印看到执行可到达到 2-8秒,整个下单接口中不知一次的数据库增删改查操作,导致接口执行时间可长达2分钟左右,是什么原因导致的?
2.我的连接池是否启用成功,并起到了它的作用?慢查询又是否是成功开启状态?
3.对于目前项目在高峰期时遇到的这种情况,响应过慢,影响体验,该如何解决应对?
【以下回答由 GPT 生成】
根据您的描述,您遇到的问题是在高并发时,使用Mongoose进行数据库查询时响应变慢的情况。在排查问题时,您已经尝试了设置连接池大小,但问题仍未得到解决。下面是一些可能的解决方案和建议:
index
选项来创建索引,例如:const vipCodeSchema = new mongoose.Schema({
vip: { type: String, index: true },
// 其他字段
});
findOne
方法,而不是find
方法。根据您的代码,似乎只需要查询满足条件的第一条记录,可以修改查询代码如下:let vipIsResult = await vipCodeSM.findOne({vip}, {code: 1}).lean();
注意,这里添加了.lean()
方法,它会返回纯JavaScript对象,而不是Mongoose文档对象,以减少一些额外的开销。
useCreateIndex: true
和useNewUrlParser: true
选项来重新定义连接配置,例如:mongoose.connect('mongodb://localhost/mydatabase', {
poolSize: 50,
useCreateIndex: true,
useNewUrlParser: true
});
确保在代码的顶部只调用一次mongoose.connect()
方法,以避免在每个请求中重复创建连接。
使用缓存: 如果您的查询结果是可以缓存的,并且在每个请求中都是相同的,您可以考虑使用缓存来减少对数据库的实际查询。您可以使用类似Redis这样的缓存服务器,将查询结果缓存在内存中,并在下次请求相同查询时直接返回缓存的结果。
分析和优化查询语句: 使用MongoDB的explain
方法可以帮助您分析查询语句的性能。您可以调用explain()
方法来查看查询的执行计划,以确定是否需要调整查询或添加索引。
let explainResult = await vipCodeSM.findOne({vip}, {code: 1}).explain();
console.log(explainResult);
这些是一些可能的解决方案和建议,您可以根据实际情况尝试其中的一些方法来优化Mongoose在高并发时的数据库查询速度。如果以上方法都没有解决您的问题,可能需要进一步分析系统的整体性能瓶颈,例如服务器性能、网络带宽等方面是否会影响查询的响应速度。如果您需要进一步的帮助,请提供更多关于系统架构和系统监控的信息,以便更精确地定位和解决问题。