高并发场景下的优化策略
高并发场景下的挑战
高并发场景下,系统面临的主要挑战包括资源竞争、数据库连接池耗尽、响应时间变长等问题。以Mongoose为例,当大量请求同时访问MongoDB时,连接管理不当会导致性能急剧下降。典型的症状包括请求排队、超时错误增多,甚至服务不可用。
连接池优化
Mongoose默认连接池大小为5,这在低并发时足够,但高并发场景下需要调整。通过poolSize
参数可以增加连接数量:
mongoose.connect('mongodb://localhost/test', {
poolSize: 50, // 增加到50个连接
socketTimeoutMS: 30000,
connectTimeoutMS: 30000
});
但要注意,连接数并非越大越好。需要根据服务器内存和负载测试结果确定最佳值。监控指标包括:
- 连接等待队列长度
- 平均连接获取时间
- 活跃连接数峰值
查询优化策略
索引优化
确保高频查询字段都有索引。通过explain()
分析查询执行计划:
const explain = await Model.find({ status: 'active' })
.sort({ createdAt: -1 })
.limit(100)
.explain('executionStats');
重点关注:
totalDocsExamined
:扫描文档数executionTimeMillis
:执行时间stage
:查询阶段类型
批量操作替代循环
避免在循环中执行单条数据库操作:
// 错误做法
for (const item of items) {
await Model.create(item);
}
// 正确做法
await Model.insertMany(items, { ordered: false });
ordered: false
允许部分失败时继续执行剩余操作。
缓存层实现
查询结果缓存
使用Redis缓存高频查询结果:
async function getProducts(category) {
const cacheKey = `products:${category}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
const data = await Product.find({ category });
await redis.setex(cacheKey, 3600, JSON.stringify(data));
return data;
}
缓存失效策略要考虑:
- 定时过期(TTL)
- 数据变更时主动清除
- 缓存击穿防护
读写分离
配置Mongoose使用不同的连接处理读写操作:
const readDB = mongoose.createConnection('mongodb://read1,read2/test', {
readPreference: 'secondaryPreferred'
});
const writeDB = mongoose.createConnection('mongodb://primary/test');
查询时根据场景选择连接:
// 写入操作
await writeDB.model('User').create(data);
// 读取操作
await readDB.model('User').find({ active: true });
批量写入优化
使用bulkWrite处理大量写入:
const ops = changes.map(change => ({
updateOne: {
filter: { _id: change.id },
update: { $set: change.fields }
}
}));
await Model.bulkWrite(ops, { ordered: false });
参数说明:
ordered: false
:无序执行,失败操作不影响后续bypassDocumentValidation: true
:跳过文档验证提升速度
事务控制
对于需要ACID保证的操作,使用事务:
const session = await mongoose.startSession();
session.startTransaction();
try {
await Order.create([orderData], { session });
await Inventory.updateOne(
{ productId: orderData.productId },
{ $inc: { quantity: -orderData.quantity } },
{ session }
);
await session.commitTransaction();
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
注意事项:
- 事务会显著降低吞吐量
- 仅在必要时使用
- 控制事务持续时间
连接监控与调优
使用Mongoose内置事件监控连接状态:
mongoose.connection.on('connected', () => {
console.log('MongoDB connected');
});
mongoose.connection.on('disconnected', () => {
console.log('MongoDB disconnected');
});
mongoose.connection.on('fullsetup', () => {
console.log('All replica set members connected');
});
关键指标监控:
- 连接池等待队列长度
- 平均查询执行时间
- 错误率变化趋势
分页查询优化
避免使用skip/limit进行深度分页:
// 低效做法
const page = await Model.find({})
.skip(10000)
.limit(10);
// 高效做法(基于范围查询)
const lastDoc = await Model.findById(lastId);
const page = await Model.find({ _id: { $gt: lastDoc._id } })
.sort({ _id: 1 })
.limit(10);
对于需要随机访问的分页,考虑:
- 预计算分页键
- 使用物化视图
- 缓存热门页数据
模式设计优化
适当反规范化
高频访问的关联数据可以内嵌:
const userSchema = new Schema({
name: String,
recentOrders: [{
orderId: Schema.Types.ObjectId,
amount: Number,
date: Date
}]
});
权衡考虑:
- 读取性能 vs 写入复杂度
- 数据一致性要求
- 更新频率
分桶模式
对于时间序列数据,使用分桶存储:
const metricsSchema = new Schema({
date: Date,
hour: Number,
readings: [{
timestamp: Date,
value: Number
}],
stats: {
avg: Number,
max: Number,
min: Number
}
});
优势:
- 减少文档数量
- 预聚合提升查询速度
- 更好的数据局部性
性能测试与监控
建立基准测试套件:
const { performance } = require('perf_hooks');
async function runBenchmark() {
const start = performance.now();
await testQuery();
const duration = performance.now() - start;
recordMetric('query_time', duration);
}
监控关键指标:
- 95/99百分位响应时间
- 数据库CPU/内存使用率
- 慢查询日志分析
- 连接池利用率
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:数据迁移与版本控制