Посібник з управління базами даних MongoDB та оптимізації запитів

Основна налаштування та з'єднання

const mongoose = require('mongoose');  
// З'єднання з обробкою помилок  
mongoose.connect('mongodb://localhost:27017/myapp', {  
 useNewUrlParser: true,  
 useUnifiedTopology: true  
}).then(() => {  
 console.log('Підключено до MongoDB');  
}).catch(err => {  
 console.error('Помилка підключення до MongoDB:', err);  
});

Кращі практики проєктування схеми

const userSchema = new mongoose.Schema({  
 username: {  
 type: String,  
 required: true,  
 unique: true,  
 index: true // Додаємо індекс для полів, які часто запитуються  
 },  
 email: {  
 type: String,  
 required: true,  
 unique: true  
 },  
 posts: [{  
 type: mongoose.Schema.Types.ObjectId,  
 ref: 'Post'  
 }],  
 metadata: {  
 lastLogin: Date,  
 loginCount: Number  
 }  
}, {  
 timestamps: true // Автоматично додає createdAt та updatedAt  
});

Ефективні запити

Основні запити

// Пошук з умовами  
const users = await User.find({  
 age: { $gte: 18 },  
 'metadata.loginCount': { $gt: 5 }  
}).select('username email'); // Вибір конкретних полів  
// Складні запити з кількома умовами  
const powerUsers = await User.find({  
 $and: [  
 { 'metadata.loginCount': { $gt: 10 } },  
 { posts: { $exists: true, $not: { $size: 0 } } }  
 ]  
});

Конвеєр агрегацій

const results = await User.aggregate([  
 // Етап фільтрації - відбір документів  
 { $match: { age: { $gte: 18 } } },  
// Етап групування - групування та підрахунок  
 { $group: {  
 _id: '$country',  
 averageAge: { $avg: '$age' },  
 totalUsers: { $sum: 1 }  
 }},  
 // Етап сортування  
 { $sort: { totalUsers: -1 } }  
]);

Реалізація пагінації

Пагінація з використанням курсора

async function getCursorPaginatedResults(cursor, limit = 10) {  
 const query = cursor  
 ? { _id: { $gt: cursor } }  
 : {};  
const items = await Collection.find(query)  
 .sort({ _id: 1 })  
 .limit(limit + 1); // Отримуємо один додатковий елемент для перевірки на наявність більше результатів  
 const hasMore = items.length > limit;  
 const results = hasMore ? items.slice(0, -1) : items;  
 return {  
 items: results,  
 nextCursor: hasMore ? results[results.length - 1]._id : null  
 };  
}

Пагінація з використанням відступу

async function getPagedResults(page = 1, limit = 10) {  
 const skip = (page - 1) * limit;  
const [items, totalCount] = await Promise.all([  
 Collection.find({})  
 .skip(skip)  
 .limit(limit)  
 .sort({ createdAt: -1 }),  
 Collection.countDocuments({})  
 ]);  
 return {  
 items,  
 currentPage: page,  
 totalPages: Math.ceil(totalCount / limit),  
 totalItems: totalCount  
 };  
}

Оптимізація запитів

Створення індексів

// Індекс для одного поля  
userSchema.index({ email: 1 });  
// Композитний індекс  
userSchema.index({ username: 1, createdAt: -1 });  
// Текстовий індекс для пошуку  
userSchema.index({  
 username: 'text',  
 bio: 'text'  
});

Використання Explain для аналізу запитів

const explanation = await User.find({  
 username: /john/i  
}).explain('executionStats');  
console.log(explanation.executionStats);

Управління даними та їх обслуговування

Резервне копіювання та відновлення

# Резервне копіювання  
mongodump --db=myapp --out=/backup/path  
# Відновлення  
mongorestore --db=myapp /backup/path/myapp

Валідація даних

const productSchema = new mongoose.Schema({  
 name: {  
 type: String,  
 required: true,  
 validate: {  
 validator: function(v) {  
 return v.length >= 3;  
 },  
 message: 'Назва продукту має містити не менше 3 символів'  
 }  
 },  
 price: {  
 type: Number,  
 min: [0, 'Ціна не може бути від’ємною']  
 }  
});

Поради щодо продуктивності

1.

Використання проєкцій

 // Погано - отримує всі поля  
await User.findOne({ _id: userId });  
// Добре - отримує лише необхідні поля  
await User.findOne({ _id: userId })  
 .select('username email');

Пакетні операції

 // Використовуйте пакетні операції для кількох оновлень  
const bulk = User.collection.initializeUnorderedBulkOp();  
users.forEach(user => {  
 bulk.find({ _id: user._id })  
 .updateOne({ $set: { status: 'active' } });  
});  
await bulk.execute();

Кешування часто запитуваних даних

const cache = new Map();  
async function getUserWithCache(userId) {  
 if (cache.has(userId)) {  
 return cache.get(userId);  
 }  
 const user = await User.findById(userId);  
 cache.set(userId, user);  
 return user;  
}

Список перевірки для моніторингу та обслуговування

Регулярний моніторинг

  • Моніторити продуктивність запитів
  • Перевіряти використання індексів
  • Моніторити вільний простір на диску
  • Відстежувати використання пулу з’єднань

    Обслуговування бази даних

  • Регулярне створення резервних копій

  • Оптимізація індексів

  • Стратегії архівації даних

  • Ротація журналів

Заходи безпеки

  • Контроль доступу на основі ролей
  • Мережна безпека
  • Регулярні перевірки безпеки
  • Шифрування за допомогою SSL/TLS

Пам’ятайте: управління базою даних — це безперервний процес. Регулярно переглядайте та оптимізуйте конструкцію вашої бази даних, запити та процедури обслуговування відповідно до змінних потреб вашого застосунку.

Перекладено з: MongoDB Database Management & Query Optimization Guide

Leave a Reply

Your email address will not be published. Required fields are marked *