Обеспечьте максимальную надежность хранения данных с помощью транзакций MongoDB
Создание готового к производству веб-приложения требует обеспечения его безопасности и масштабируемости
Одним из наиболее важных моментов, которые необходимо знать о базах данных, является принцип ACID, который расшифровывается как атомарность, согласованность, изоляция и долговечность. Реляционные базы данных, такие как MySQL, поддерживают транзакции ACID. Но MongoDB является NoSQL базой данных и по умолчанию не поддерживает ACID транзакции
Как программист, вы должны знать, как внедрить свойства ACID в ваши базы данных MongoDB
Что такое транзакции базы данных?
Транзакция базы данных – это последовательность запросов или операций базы данных, которые выполняются как единое целое для выполнения одной задачи
Транзакции базы данных придерживаются концепции ACID-характеристик. Это позволяет гарантировать, что никаких изменений не произойдет, пока все операции не будут выполнены успешно. Это также обеспечивает согласованность базы данных
Объяснение свойств ACID
Четыре свойства, составляющие принципы ACID, следующие:
Как реализовать транзакции базы данных MongoDB в Node.js с помощью Mongoose
MongoDB с годами стала широко используемой технологией баз данных благодаря своей природе NoSQL и гибкой модели на основе документов. Он также предлагает вам возможность лучше организовать ваши данные и более гибко, чем в SQL или реляционных базах данных
Для реализации транзакций базы данных в MongoDB можно рассмотреть примерный сценарий приложения для размещения объявлений о вакансиях, где пользователь может разместить, обновить или удалить вакансию. Вот простой дизайн схемы базы данных для этого приложения:
Чтобы следовать дальше, этот раздел требует базовых знаний программирования Node.js и MongoDB
Транзакции не поддерживаются в автономных установках MongoDB. Для работы транзакций необходимо использовать набор реплик MongoDB или кластер MongoDB sharded. Поэтому самый простой способ использовать транзакции – это создать экземпляр MongoDB, размещенный в облаке (MongoDB Atlas). По умолчанию каждый экземпляр базы данных Atlas представляет собой набор реплик или кластер с черепками
Создав рабочий проект Node.js и MongoDB, вы можете установить соединение с базой данных Mongo в Node.js. Если вы не сделали этого раньше, установите mongoose, выполнив npm install mongoose в терминале
import mongoose from 'mongoose'
let MONGO_URL = process.env.MONGO_URL || 'your-mongo-database-url';
let connection;
const connectDb = async () => {
try {
await mongoose.connect(MONGO_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log('CONNECTED TO DATABASE');
connection = mongoose.connection;
} catch (err) {
console.error('DATABASE CONNECTION FAILED!');
console.error(err.message);
process.exit(1); // close the app if database connection fails
}
};
Вы должны сохранить соединение в переменной, чтобы вы могли использовать его для инициирования транзакции позже в программе
Вы можете реализовать коллекции пользователей и заданий следующим образом:
const userSchema = new mongoose.Schema({
name: String,
email: String,
jobs: [mongoose.Schema.Types.ObjectId]
});
const jobSchema = new mongoose.Schema({
title: String,
location: String,
salary: String,
poster: mongoose.Schema.Types.ObjectId
});
const userCollection = mongoose.model('user', userSchema);
const jobCollection = mongoose.model('job', jobSchema);
Вы можете написать функцию для добавления пользователя в базу данных следующим образом:
const createUser = async (user) => {
const newUser = await userCollection.create(user);
console.log('User added to database');
console.log(newUser);
}
Приведенный ниже код демонстрирует функцию создания задания и добавления его в список заданий плаката с использованием транзакции базы данных
const createJob = async (job) => {
const { userEmail, title, location, salary } = job;
// get the user from the DB
const user = await userCollection.findOne({ email: userEmail });
// start transaction session
const session = await connection.startSession();
// run all database queries in a try-catch block
try {
await session.startTransaction();
// create job
const newJob = await jobCollection.create(
[
{
title,
location,
salary,
poster: user._id,
},
],
{ session }
);
console.log('Created new job successfully!');
console.log(newJob[0]);
// add job to users list of posted jobs
const newJobId = newJob[0]._id;
const addedToUser = await userCollection.findByIdAndUpdate(
user._id,
{ $addToSet: { jobs: newJobId } },
{ session }
);
console.log('Successfully added job to user's jobs list');
console.log(addedToUser);
await session.commitTransaction();
console.log('Successfully carried out DB transaction');
} catch (e) {
console.error(e);
console.log('Failed to complete database operations');
await session.abortTransaction();
} finally {
await session.endSession();
console.log('Ended transaction session');
}
};
Запрос create, выполняемый в транзакции, обычно принимает и возвращает массив. Вы можете увидеть это в приведенном выше коде, где он создает newJob и сохраняет его свойство _id в переменной newJobId
Вот демонстрация того, как работают вышеупомянутые функции:
const mockUser = {
name: 'Timmy Omolana',
email: 'jobposter@example.com',
};
const mockJob = {
title: 'Sales Manager',
location: 'Lagos, Nigeria',
salary: '$40,000',
userEmail: 'jobposter@example.com', // email of the created user
};
const startServer = async () => {
await connectDb();
await createUser(mockUser);
await createJob(mockJob);
};
startServer()
then()
catch((err) => console.log(err));
Если вы сохраните этот код и запустите его с помощью npm start или команды node, он должен выдать результат, подобный этому:
Другой способ реализации ACID-транзакций в MongoDB с помощью Mongoose – это использование функции withTransaction(). Этот подход обеспечивает небольшую гибкость, поскольку все запросы выполняются внутри функции обратного вызова, которую вы передаете в качестве аргумента функции
Вы можете рефакторизовать приведенную выше транзакцию базы данных для использования функции withTransaction() следующим образом:
const createJob = async (job) => {
const { userEmail, title, location, salary } = job;
// get the user from the DB
const user = await userCollection.findOne({ email: userEmail });
// start transaction session
const session = await connection.startSession();
// run all database queries in a try-catch block
try {
const transactionSuccess = await session.withTransaction(async () => {
const newJob = await jobCollection.create(
[
{
title,
location,
salary,
poster: user._id,
},
],
{ session }
);
console.log('Created new job successfully!');
console.log(newJob[0]);
// add job to users list of posted jobs
const newJobId = newJob[0]._id;
const addedToUser = await userCollection.findByIdAndUpdate(
user._id,
{ $addToSet: { jobs: newJobId } },
{ session }
);
console.log('Successfully added job to user's jobs list');
console.log(addedToUser);
});
if (transactionSuccess) {
console.log('Successfully carried out DB transaction');
} else {
console.log('Transaction failed');
}
} catch (e) {
console.error(e);
console.log('Failed to complete database operations');
} finally {
await session.endSession();
console.log('Ended transaction session');
}
};
Это даст тот же результат, что и предыдущая реализация. Вы вольны выбирать, какой стиль использовать при реализации транзакций базы данных в MongoDB
В этой реализации не используются функции commitTransaction() и abortTransaction(). Это связано с тем, что функция withTransaction() автоматически фиксирует успешные транзакции и прерывает неудачные. Единственная функция, которую вы должны вызывать во всех случаях – это функция session.endSession()
Реализация ACID транзакций базы данных в MongoDB
Транзакции баз данных просты в использовании, если они выполнены правильно. Теперь вы должны понимать, как работают транзакции баз данных в MongoDB и как их можно реализовать в приложениях Node.js
Для дальнейшего изучения идеи транзакций ACID и того, как они работают в MongoDB, рассмотрите возможность создания финтех-кошелька или приложения для ведения блогов
Комментировать