MongoDB代码实践:从基础操作到高级应用
MongoDB作为非关系型数据库的代表,以其灵活的文档模型和强大的横向扩展能力,成为现代应用开发的重要选择。本文将从基础CRUD操作出发,逐步深入索引优化、聚合查询、事务处理等高级场景,结合实际代码示例与性能优化建议,帮助开发者构建高效可靠的MongoDB应用。
一、基础CRUD操作:代码规范与最佳实践
1.1 连接管理与错误处理
MongoDB的Node.js驱动提供了直观的连接API,但需注意连接池配置与错误重试机制:
const { MongoClient } = require('mongodb');const uri = 'mongodb://localhost:27017';const client = new MongoClient(uri, {maxPoolSize: 50, // 连接池大小retryWrites: true, // 写操作重试retryReads: true // 读操作重试});async function connect() {try {await client.connect();console.log('Connected to MongoDB');return client.db('testDB');} catch (err) {console.error('Connection error:', err);process.exit(1);}}
关键点:
- 连接池大小需根据并发量调整(建议10-100)
- 生产环境应配置
connectTimeoutMS和socketTimeoutMS - 使用
async/await避免回调地狱
1.2 文档操作代码示例
插入文档:
async function insertUser(db, userData) {const collection = db.collection('users');const result = await collection.insertOne({...userData,createdAt: new Date(),updatedAt: new Date()});return result.insertedId;}
查询文档:
async function findUserById(db, userId) {const collection = db.collection('users');return await collection.findOne({ _id: new ObjectId(userId) });}
更新文档:
async function updateUserEmail(db, userId, newEmail) {const collection = db.collection('users');const result = await collection.updateOne({ _id: new ObjectId(userId) },{ $set: { email: newEmail, updatedAt: new Date() } });return result.modifiedCount;}
删除文档:
async function deleteUser(db, userId) {const collection = db.collection('users');const result = await collection.deleteOne({ _id: new ObjectId(userId) });return result.deletedCount;}
最佳实践:
- 始终使用
ObjectId处理_id字段 - 批量操作优先选择
insertMany/updateMany - 更新时使用
$set避免覆盖整个文档
二、索引优化:代码实现与性能分析
2.1 索引创建与管理
async function createIndexes(db) {const collection = db.collection('orders');await collection.createIndexes([{ key: { userId: 1 }, name: 'userId_idx' }, // 单字段索引{ key: { status: 1, createdAt: -1 }, name: 'status_createdAt_idx' }, // 复合索引{ key: { 'address.city': 1 }, name: 'city_idx' }, // 嵌套字段索引{ key: { email: 1 }, unique: true } // 唯一索引]);}
索引策略:
- 查询频率高的字段优先建索引
- 复合索引遵循”最左前缀”原则
- 写密集型场景需权衡索引数量(建议不超过5个/集合)
2.2 索引性能监控
async function analyzeIndexes(db) {const collection = db.collection('orders');const stats = await collection.stats();const indexStats = await collection.indexes();console.log('Index sizes:', indexStats.map(idx => ({name: idx.name,size: idx.stats?.size || 'N/A'})));// 解释查询执行计划const explain = await collection.find({ status: 'pending' }).explain('executionStats');console.log('Query execution stats:', explain.executionStats);}
优化建议:
- 使用
explain()分析查询是否命中索引 - 定期运行
db.collection.aggregate([{ $indexStats: {} }])监控索引使用率 - 删除未使用的索引(可通过
$indexStats识别)
三、聚合查询:复杂场景代码实现
3.1 基础聚合管道
async function getUserOrderStats(db, userId) {const pipeline = [{ $match: { userId: new ObjectId(userId) } },{ $group: {_id: '$status',count: { $sum: 1 },totalAmount: { $sum: '$amount' }}},{ $sort: { totalAmount: -1 } },{ $project: {status: '$_id',count: 1,totalAmount: 1,_id: 0}}];return await db.collection('orders').aggregate(pipeline).toArray();}
3.2 高级聚合技巧
日期范围统计:
async function getDailySales(db, startDate, endDate) {const pipeline = [{ $match: {createdAt: { $gte: new Date(startDate), $lte: new Date(endDate) }}},{ $project: {date: { $dateToString: { format: '%Y-%m-%d', date: '$createdAt' } },amount: 1}},{ $group: {_id: '$date',total: { $sum: '$amount' },avg: { $avg: '$amount' }}},{ $sort: { _id: 1 } }];return await db.collection('orders').aggregate(pipeline).toArray();}
优化建议:
- 复杂聚合前先用
$match减少处理数据量 - 合理使用
$project控制字段数量 - 内存密集型操作(如
$sort)需设置allowDiskUse: true
四、事务处理:多文档操作代码示例
4.1 会话管理实现
async function transferFunds(db, fromId, toId, amount) {const session = db.client.startSession();try {session.startTransaction({readConcern: { level: 'local' },writeConcern: { w: 'majority' }});const accounts = db.collection('accounts');const fromAccount = await accounts.findOne({_id: new ObjectId(fromId),balance: { $gte: amount }}, { session });if (!fromAccount) throw new Error('Insufficient funds');await accounts.updateOne({ _id: new ObjectId(fromId) },{ $inc: { balance: -amount } },{ session });await accounts.updateOne({ _id: new ObjectId(toId) },{ $inc: { balance: amount } },{ session });await session.commitTransaction();return true;} catch (err) {await session.abortTransaction();console.error('Transaction failed:', err);throw err;} finally {session.endSession();}}
4.2 事务使用准则
适用场景:
- 多文档原子性操作
- 跨集合更新
- 金融类敏感操作
注意事项:
- 事务操作会增加服务器负载
- 单个事务中操作建议不超过1000个文档
- 生产环境应配置适当的
readConcern和writeConcern
五、性能优化:代码级调优策略
5.1 查询优化技巧
// 优化前:全表扫描db.collection('products').find({ price: { $gt: 100 } });// 优化后:使用索引db.collection('products').find({price: { $gt: 100 },category: 'electronics' // 添加选择性高的过滤条件}).sort({ price: 1 });
优化建议:
- 使用
$gt/$lt等范围查询时,确保索引顺序正确 - 避免在查询条件中使用计算字段(如
{$gt: [new Date(), -86400000]}) - 投影只返回必要字段
5.2 批量操作优化
// 低效方式:循环单条插入async function inefficientInsert(db, users) {for (const user of users) {await db.collection('users').insertOne(user);}}// 高效方式:批量插入async function efficientInsert(db, users) {const chunks = [];for (let i = 0; i < users.length; i += 1000) {chunks.push(users.slice(i, i + 1000));}for (const chunk of chunks) {await db.collection('users').insertMany(chunk);}}
批量操作建议:
- 批量大小控制在500-1000条/次
- 错误处理需区分部分失败和全部失败
- 使用
ordered: false提高并行度(允许部分失败)
六、安全实践:代码防护措施
6.1 防注入攻击
// 危险方式:直接拼接查询function unsafeFind(db, email) {return db.collection('users').find({ email }); // 若email来自用户输入}// 安全方式:使用参数化查询async function safeFind(db, email) {// MongoDB驱动默认使用参数化查询// 但需验证输入格式if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {throw new Error('Invalid email format');}return await db.collection('users').findOne({ email });}
6.2 敏感数据保护
async function getUserProfile(db, userId, isAdmin) {const projection = isAdmin ? {} : {password: 0,ssn: 0,creditCard: 0};return await db.collection('users').findOne({ _id: new ObjectId(userId) }, { projection });}
安全建议:
- 实施最小权限原则
- 使用字段投影排除敏感数据
- 定期轮换API密钥和数据库凭证
七、部署与运维:代码相关实践
7.1 变更流(Change Streams)实现
async function watchCollectionChanges(db) {const collection = db.collection('inventory');const changeStream = collection.watch();changeStream.on('change', (change) => {console.log('Change detected:', change);// 可根据change.operationType处理不同操作});// 保持连接活跃setInterval(() => {}, 10000);}
应用场景:
- 实时数据同步
- 触发微服务事件
- 审计日志记录
7.2 连接字符串加密
// 生产环境应从安全存储获取凭据const encryptedUri = process.env.MONGODB_ENCRYPTED_URI;const decryptionKey = process.env.DECRYPTION_KEY;// 实际应用中应使用专业的密钥管理系统function decryptUri(encrypted, key) {// 伪代码:实际实现需使用AES等加密算法return encrypted.replace('__ENC__', '');}const uri = decryptUri(encryptedUri, decryptionKey);const client = new MongoClient(uri);
总结
MongoDB的代码实现需要兼顾功能实现与性能优化。从基础CRUD操作到高级事务处理,每个环节都存在优化空间。开发者应遵循以下原则:
- 合理设计文档结构减少更新操作
- 通过索引策略平衡查询性能与写入开销
- 利用聚合框架处理复杂分析需求
- 实施严格的安全防护措施
- 结合业务场景选择合适的部署架构
实际应用中,建议通过压力测试验证代码性能,并持续监控数据库指标。对于高并发场景,可考虑分片集群部署;对于数据一致性要求高的业务,需仔细设计事务边界。随着MongoDB版本升级(如5.0+的时间序列集合、6.0+的集群查询优化),开发者应保持对新技术特性的关注,及时调整代码实现以获得最佳性能。