阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 高并发场景下的优化策略

高并发场景下的优化策略

作者:陈川 阅读数:23918人阅读 分类: MongoDB

高并发场景下的挑战

高并发场景下,系统面临的主要挑战包括资源竞争、数据库连接池耗尽、响应时间变长等问题。以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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌