5月29日 01:08
What are the best practices for Mongoose performance optimization?
Mongoose performance optimization is key to developing efficient applications. Through proper configuration and best practices, you can significantly improve query speed and overall performance.
Connection Optimization
Connection Pool Configuration
javascriptmongoose.connect('mongodb://localhost:27017/mydb', { maxPoolSize: 100, // Maximum connections minPoolSize: 10, // Minimum connections socketTimeoutMS: 45000, // Socket timeout serverSelectionTimeoutMS: 5000, // Server selection timeout connectTimeoutMS: 10000 // Connection timeout });
Connection Reuse
javascript// Establish connection when application starts mongoose.connect('mongodb://localhost:27017/mydb'); // Don't frequently close and reconnect // Avoid creating new connections on every request
Index Optimization
Creating Indexes
javascriptconst userSchema = new Schema({ email: { type: String, index: true, // Single field index unique: true }, name: { type: String, index: true }, age: Number, status: String }); // Compound index userSchema.index({ status: 1, age: -1 }); // Text index userSchema.index({ name: 'text', bio: 'text' }); // Geospatial index userSchema.index({ location: '2dsphere' });
Index Strategy
- Create indexes for frequently queried fields
- Use compound indexes to optimize multi-field queries
- Avoid too many indexes affecting write performance
- Regularly analyze query performance and optimize indexes
javascript// Analyze query plan const query = User.find({ email: 'john@example.com' }); const explanation = await query.explain('executionStats'); console.log(explanation.executionStats);
Query Optimization
Use lean()
javascript// Return plain JavaScript objects, better performance const users = await User.find().lean(); // Use lean() for read-only queries const users = await User.find({ status: 'active' }).lean();
Selective Querying
javascript// Only query needed fields const users = await User.find() .select('name email age') .lean(); // Exclude large fields const users = await User.find() .select('-largeField -anotherLargeField');
Limit Result Count
javascript// Use limit to restrict returned count const users = await User.find() .limit(100); // Implement pagination const page = 1; const pageSize = 20; const users = await User.find() .skip((page - 1) * pageSize) .limit(pageSize);
Use Projection
javascript// Projection reduces data transfer const users = await User.find( { status: 'active' }, { name: 1, email: 1, _id: 0 } );
Batch Operations
Batch Insert
javascript// Use insertMany instead of multiple insertOne const users = await User.insertMany([ { name: 'John', email: 'john@example.com' }, { name: 'Jane', email: 'jane@example.com' }, // ... more users ]);
Batch Update
javascript// Use updateMany instead of multiple updateOne await User.updateMany( { status: 'pending' }, { status: 'active' } );
Batch Delete
javascript// Use deleteMany instead of multiple deleteOne await User.deleteMany({ status: 'deleted' });
Caching Strategy
Query Caching
javascriptconst userSchema = new Schema({ name: String, email: String }, { query: { cache: true } }); // Enable caching const users = await User.find().cache(); // Set cache time const users = await User.find().cache(60); // 60 seconds
Application Layer Caching
javascriptconst NodeCache = require('node-cache'); const cache = new NodeCache({ stdTTL: 600 }); // 10 minute cache async function getUserById(userId) { const cacheKey = `user:${userId}`; let user = cache.get(cacheKey); if (!user) { user = await User.findById(userId).lean(); if (user) { cache.set(cacheKey, user); } } return user; }
Data Model Optimization
Embedding vs Reference
javascript// Embedding for one-to-one or one-to-many, small child documents const userSchema = new Schema({ name: String, profile: { bio: String, avatar: String } }); // Reference for one-to-many or many-to-many, large child documents const postSchema = new Schema({ title: String, author: { type: Schema.Types.ObjectId, ref: 'User' } });
Avoid Deep Nesting
javascript// Avoid overly deep nested structures // Not recommended const badSchema = new Schema({ level1: { level2: { level3: { level4: { data: String } } } } }); // Recommended: Flatten structure const goodSchema = new Schema({ level1: String, level2: String, level3: String, level4: String });
Monitoring and Tuning
Query Performance Monitoring
javascript// Enable debug mode mongoose.set('debug', true); // Custom debug function mongoose.set('debug', (collectionName, method, query, doc) => { console.log(`${collectionName}.${method}`, JSON.stringify(query)); });
Slow Query Logging
javascript// Log slow queries mongoose.connection.on('connected', () => { mongoose.connection.db.admin().command({ profile: 1, slowms: 100 // Queries over 100ms }); });
Best Practices Summary
- Connection Management: Use connection pools, avoid frequent connect/disconnect
- Index Optimization: Create appropriate indexes for frequently queried fields
- Query Optimization: Use lean(), selective queries, limit results
- Batch Operations: Use batch operations instead of multiple single operations
- Caching Strategy: Reasonably use query cache and application layer cache
- Data Model: Choose embedding or reference based on access patterns
- Monitoring and Tuning: Continuously monitor query performance and optimize timely
- Avoid N+1 Queries: Design data structure properly, avoid loop queries