Amazon DocumentDB 性能优化 #
一、性能优化概述 #
1.1 优化维度 #
text
性能优化维度:
├── 查询优化
│ ├── 查询语句优化
│ ├── 索引使用
│ └── 执行计划分析
│
├── 索引优化
│ ├── 索引设计
│ ├── 索引维护
│ └── 索引监控
│
├── 连接优化
│ ├── 连接池配置
│ ├── 连接管理
│ └── 连接复用
│
└── 配置优化
├── 实例规格
├── 参数配置
└── 资源分配
1.2 性能基准 #
text
建立性能基准:
├── 记录正常性能指标
├── 监控关键指标
├── 设置性能阈值
├── 定期性能测试
└── 持续优化改进
二、查询优化 #
2.1 使用explain分析 #
javascript
// 分析查询执行计划
db.users.find({ email: "test@example.com" }).explain("executionStats")
// 关键指标
{
"executionStats": {
"totalDocsExamined": 1, // 扫描文档数
"totalKeysExamined": 1, // 扫描索引键数
"executionTimeMillis": 0, // 执行时间
"indexUsed": "email_1" // 使用的索引
}
}
// 理想情况
// totalDocsExamined ≈ 返回文档数
// totalKeysExamined ≈ totalDocsExamined
2.2 查询优化技巧 #
javascript
// 1. 使用索引覆盖查询
db.users.find(
{ email: "test@example.com" },
{ _id: 0, email: 1, name: 1 } // 只返回索引字段
)
// 2. 限制返回字段
db.users.find(
{ status: "active" },
{ name: 1, email: 1 } // 只返回需要的字段
)
// 3. 使用投影减少数据传输
db.orders.find(
{ status: "completed" },
{ orderNo: 1, amount: 1 }
)
// 4. 避免使用$or,使用$in代替
// 不推荐
db.users.find({
$or: [
{ status: "active" },
{ status: "pending" }
]
})
// 推荐
db.users.find({
status: { $in: ["active", "pending"] }
})
// 5. 避免使用$nin和$ne
// 不推荐
db.users.find({ status: { $ne: "deleted" } })
// 推荐
db.users.find({
status: { $in: ["active", "pending", "inactive"] }
})
2.3 排序优化 #
javascript
// 排序使用索引
db.orders.createIndex({ status: 1, createdAt: -1 })
// 使用索引排序
db.orders.find({ status: "completed" }).sort({ createdAt: -1 })
// 避免内存排序
// 如果排序字段没有索引,使用allowDiskUse
db.orders.find({}).sort({ amount: -1 }).allowDiskUse(true)
2.4 分页优化 #
javascript
// 使用游标分页代替skip
// 不推荐:深度分页性能差
db.orders.find({}).skip(10000).limit(10)
// 推荐:使用游标分页
function findWithCursor(lastId, limit = 10) {
const query = lastId
? { _id: { $gt: lastId } }
: {};
return db.orders.find(query)
.sort({ _id: 1 })
.limit(limit)
.toArray();
}
三、索引优化 #
3.1 索引设计原则 #
text
ESR规则:
├── Equality(等值查询字段在前)
├── Sort(排序字段其次)
└── Range(范围查询字段最后)
示例:
查询:{ status: "active", createdAt: { $gte: date } }
排序:{ amount: -1 }
索引:{ status: 1, amount: -1, createdAt: 1 }
3.2 索引创建策略 #
javascript
// 1. 为常用查询字段创建索引
db.users.createIndex({ email: 1 })
// 2. 创建复合索引
db.orders.createIndex({
status: 1,
createdAt: -1,
amount: -1
})
// 3. 使用部分索引减少大小
db.orders.createIndex(
{ status: 1, createdAt: -1 },
{
partialFilterExpression: {
status: { $in: ["pending", "processing"] }
}
}
)
// 4. 使用稀疏索引
db.users.createIndex(
{ nickname: 1 },
{ sparse: true }
)
3.3 索引监控 #
javascript
// 查看索引使用情况
db.users.aggregate([
{ $indexStats: {} },
{ $sort: { "accesses.ops": -1 } }
])
// 找出未使用的索引
db.users.aggregate([
{ $indexStats: {} },
{ $match: { "accesses.ops": 0 } }
])
// 查看索引大小
db.users.totalIndexSize()
3.4 索引维护 #
javascript
// 删除未使用的索引
db.users.dropIndex("unused_index_name")
// 重建索引(谨慎使用)
db.users.reIndex()
// 查看索引构建进度
db.currentOp({
$or: [
{ op: "command", "command.createIndexes": { $exists: true } },
{ op: "none", ns: /system\.indexes/ }
]
})
四、连接池优化 #
4.1 连接池配置 #
javascript
// Node.js连接池配置
const client = new MongoClient(uri, {
maxPoolSize: 100, // 最大连接数
minPoolSize: 10, // 最小连接数
maxIdleTimeMS: 60000, // 最大空闲时间
waitQueueTimeoutMS: 5000, // 等待超时
connectTimeoutMS: 10000, // 连接超时
socketTimeoutMS: 0, // Socket超时
serverSelectionTimeoutMS: 5000 // 服务器选择超时
});
4.2 连接池最佳实践 #
text
连接池建议:
├── maxPoolSize:根据实例规格设置
│ ├── db.r6g.large:50-100
│ ├── db.r6g.xlarge:100-200
│ └── db.r6g.2xlarge:200-400
│
├── minPoolSize:保持一定数量的连接
├── maxIdleTimeMS:避免长时间空闲
├── 合理设置超时时间
└── 监控连接使用情况
4.3 连接管理 #
javascript
// 连接管理最佳实践
class DatabaseManager {
constructor() {
this.client = null;
}
async connect() {
if (!this.client) {
this.client = new MongoClient(uri, {
maxPoolSize: 50,
minPoolSize: 5
});
await this.client.connect();
}
return this.client;
}
async disconnect() {
if (this.client) {
await this.client.close();
this.client = null;
}
}
getClient() {
if (!this.client) {
throw new Error('Database not connected');
}
return this.client;
}
}
// 单例模式
const dbManager = new DatabaseManager();
五、写入优化 #
5.1 批量写入 #
javascript
// 使用批量写入代替循环单条写入
// 不推荐
for (const doc of documents) {
await db.users.insertOne(doc);
}
// 推荐
await db.users.insertMany(documents);
// 批量大小建议:1000-5000
const batchSize = 1000;
for (let i = 0; i < documents.length; i += batchSize) {
const batch = documents.slice(i, i + batchSize);
await db.users.insertMany(batch, { ordered: false });
}
5.2 写关注优化 #
javascript
// 根据场景选择写关注
// 高性能场景
db.users.insertOne(
{ name: "张三" },
{ writeConcern: { w: 1 } }
);
// 高可靠场景
db.users.insertOne(
{ name: "张三" },
{ writeConcern: { w: "majority", j: true } }
);
5.3 索引影响 #
text
索引对写入的影响:
├── 每个索引增加写入开销
├── 索引越多,写入越慢
├── 批量导入时考虑删除索引
└── 导入后重建索引
六、读取优化 #
6.1 读偏好设置 #
javascript
// 读偏好设置
// 从副本读取
db.users.find({}).readPref("secondaryPreferred");
// 读偏好选项
const readPreferences = {
"primary": "只从主实例读取",
"primaryPreferred": "优先主实例",
"secondary": "只从副本读取",
"secondaryPreferred": "优先副本",
"nearest": "延迟最低的节点"
};
6.2 缓存优化 #
text
缓存策略:
├── 使用应用层缓存(Redis)
├── 缓存热点数据
├── 设置合理的过期时间
├── 实现缓存更新机制
└── 监控缓存命中率
6.3 查询结果缓存 #
javascript
// 实现简单的查询缓存
class QueryCache {
constructor(ttl = 60000) {
this.cache = new Map();
this.ttl = ttl;
}
async get(key, queryFn) {
const cached = this.cache.get(key);
if (cached && Date.now() - cached.time < this.ttl) {
return cached.data;
}
const data = await queryFn();
this.cache.set(key, { data, time: Date.now() });
return data;
}
invalidate(key) {
this.cache.delete(key);
}
}
// 使用示例
const cache = new QueryCache();
const users = await cache.get('active_users', () =>
db.users.find({ status: 'active' }).toArray()
);
七、配置优化 #
7.1 实例规格选择 #
| 场景 | 推荐规格 | 说明 |
|---|---|---|
| 开发测试 | db.t3.medium | 成本优化 |
| 小型生产 | db.r6g.large | 入门级生产 |
| 中型生产 | db.r6g.xlarge | 中等负载 |
| 大型生产 | db.r6g.2xlarge | 高负载 |
| 超大规模 | db.r6g.4xlarge | 极高负载 |
7.2 副本配置 #
text
副本配置建议:
├── 生产环境至少2个副本
├── 跨可用区部署
├── 根据读负载调整副本数
├── 监控副本延迟
└── 合理设置故障转移优先级
7.3 参数优化 #
bash
# 常用参数优化
# 慢查询阈值
aws docdb modify-db-cluster-parameter-group \
--db-cluster-parameter-group-name custom-params \
--parameters "ParameterName=slow_op_threshold_ms,ParameterValue=50,ApplyMethod=immediate"
# 启用性能分析
aws docdb modify-db-cluster \
--db-cluster-identifier my-cluster \
--enable-cloudwatch-logs-exports '["profiler"]' \
--apply-immediately
八、监控与诊断 #
8.1 性能监控指标 #
text
关键监控指标:
├── CPUUtilization - CPU使用率
├── FreeableMemory - 可用内存
├── DatabaseConnections - 连接数
├── ReadLatency/WriteLatency - 读写延迟
├── BufferCacheHitRatio - 缓存命中率
├── ReplicationLag - 复制延迟
└── QueryThroughput - 查询吞吐量
8.2 慢查询分析 #
javascript
// 分析慢查询
db.system.profile.find({
millis: { $gt: 100 }
}).sort({ ts: -1 }).limit(10)
// 聚合分析慢查询
db.system.profile.aggregate([
{ $match: { millis: { $gt: 100 } } },
{ $group: {
_id: "$command.find",
count: { $sum: 1 },
avgTime: { $avg: "$millis" },
maxTime: { $max: "$millis" }
}},
{ $sort: { avgTime: -1 } }
])
8.3 性能诊断工具 #
text
诊断工具:
├── explain() - 查询计划分析
├── $indexStats - 索引使用统计
├── CloudWatch - 性能监控
├── Performance Insights - 性能洞察
└── 审计日志 - 操作审计
九、性能测试 #
9.1 基准测试 #
javascript
// 简单的性能测试
async function benchmarkQuery(query, iterations = 100) {
const times = [];
for (let i = 0; i < iterations; i++) {
const start = Date.now();
await query();
times.push(Date.now() - start);
}
return {
avg: times.reduce((a, b) => a + b) / times.length,
min: Math.min(...times),
max: Math.max(...times),
p95: times.sort((a, b) => a - b)[Math.floor(times.length * 0.95)]
};
}
// 使用示例
const result = await benchmarkQuery(() =>
db.users.find({ email: "test@example.com" }).toArray()
);
console.log(result);
9.2 负载测试 #
text
负载测试建议:
├── 模拟真实场景
├── 逐步增加负载
├── 监控关键指标
├── 记录性能数据
└── 分析瓶颈
十、总结 #
10.1 优化要点 #
| 维度 | 优化方法 |
|---|---|
| 查询 | 使用索引、优化语句、减少扫描 |
| 索引 | 合理设计、定期维护、监控使用 |
| 连接 | 连接池、复用连接、监控连接 |
| 配置 | 合适规格、参数优化、资源分配 |
10.2 最佳实践总结 #
text
性能优化最佳实践:
├── 建立性能基准
├── 持续监控分析
├── 定期优化调整
├── 测试验证效果
└── 文档记录经验
下一步,让我们学习迁移指南!
最后更新:2026-03-27