Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
ACID — принцип надёжности баз данных
ACID — это набор из четырёх фундаментальных свойств, которые гарантируют надёжность и целостность данных в системах управления базами данных. Это акроним для Atomicity, Consistency, Isolation, Durability.
A — Atomicity (Атомарность)
Атомарность означает, что транзакция либо выполняется полностью, либо не выполняется вообще. Нет частичного выполнения.
// Пример: перевод денег со счёта А на счёт Б
async function transferMoney(fromAccountId: number, toAccountId: number, amount: number) {
const db = getDatabase();
try {
// Начинаем транзакцию
await db.query('BEGIN TRANSACTION');
// Снимаем со счёта А
await db.query(
'UPDATE accounts SET balance = balance - $1 WHERE id = $2',
[amount, fromAccountId]
);
// Добавляем на счёт Б
await db.query(
'UPDATE accounts SET balance = balance + $1 WHERE id = $2',
[amount, toAccountId]
);
// Коммитим изменения
await db.query('COMMIT');
} catch (error) {
// Откатываем ВСЕ изменения
await db.query('ROLLBACK');
throw error;
}
}
Почему это важно: Если произойдёт ошибка после снятия денег, но до добавления на другой счёт, деньги просто исчезнут. Атомарность гарантирует, что либо обе операции выполнены, либо ни одна.
C — Consistency (Согласованность)
Согласованность означает, что база данных переходит из одного валидного состояния в другое. Все бизнес-правила и ограничения (constraints) выполняются.
-- Пример: таблица с ограничениями целостности
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT NOT NULL,
status VARCHAR(20) CHECK (status IN ('pending', 'completed', 'cancelled')),
total_amount DECIMAL(10, 2) CHECK (total_amount > 0),
FOREIGN KEY (user_id) REFERENCES users(id)
);
База данных не позволит вставить:
- Заказ с несуществующим user_id (нарушение FK)
- Заказ с invalid status (нарушение CHECK)
- Заказ с отрицательной суммой (нарушение CHECK)
// Пример в приложении
class Order {
async create(userId: number, items: OrderItem[]): Promise<Order> {
// Бизнес-правило: заказ не может быть пустым
if (!items || items.length === 0) {
throw new InvalidOrderError('Order must have at least one item');
}
// Бизнес-правило: все товары имеют валидную цену
const hasInvalidPrice = items.some(item => item.price <= 0);
if (hasInvalidPrice) {
throw new InvalidOrderError('All items must have positive price');
}
return this.repository.save(this);
}
}
I — Isolation (Изолированность)
Изолированность означает, что конкурентные транзакции не влияют друг на друга. Изменения в одной транзакции невидимы другим, пока не будут закоммичены.
// Сценарий: две параллельные операции
// Транзакция 1 (T1)
async function buyProduct(productId: number, quantity: number) {
await db.query('BEGIN ISOLATION LEVEL SERIALIZABLE');
const product = await db.query(
'SELECT stock FROM products WHERE id = $1 FOR UPDATE',
[productId]
);
if (product.stock < quantity) {
throw new OutOfStockError();
}
await db.query(
'UPDATE products SET stock = stock - $1 WHERE id = $2',
[quantity, productId]
);
await db.query('COMMIT');
}
// Транзакция 2 (T2) начинается одновременно с T1
// Благодаря ISOLATION LEVEL, T2 не увидит изменения T1 пока та не закоммитилась
Уровни изолированности:
- READ UNCOMMITTED — может читать незакоммиченные данные (dirty read)
- READ COMMITTED — читает только закоммиченные данные (по умолчанию в большинстве БД)
- REPEATABLE READ — гарантирует, что одни и те же строки на протяжении транзакции вернут одни и те же данные
- SERIALIZABLE — самый высокий уровень, полная изолированность, но медленнее
D — Durability (Долговечность)
Долговечность означает, что закоммиченные данные не будут потеряны, даже при сбое системы (отключение питания, крах процесса и т.д.).
// Пример: сохраняем критические данные
async function saveCriticalTransaction(transaction: Transaction) {
await db.query('BEGIN');
// Все операции выполняются в памяти и в логе
await db.query(
'INSERT INTO transactions (id, amount, status) VALUES ($1, $2, $3)',
[transaction.id, transaction.amount, 'pending']
);
// COMMIT гарантирует, что данные физически записаны на диск
await db.query('COMMIT');
// Даже если сервер упадёт в следующую секунду,
// данные останутся в БД
}
Базы данных достигают долговечности через:
- Write-Ahead Logging (WAL) — запись в лог перед записью на диск
- fsync() — принудительное сброс буфера на диск
- Репликация — копирование данных на несколько серверов
Практический пример: критичная операция
async function processPayment(orderId: number, amount: number) {
const transaction = await db.transaction(async (trx) => {
// ATOMICITY: всё выполнится или ничего
// Проверяем баланс (CONSISTENCY)
const wallet = await trx('wallets')
.where('user_id', userId)
.forUpdate()
.first();
if (wallet.balance < amount) {
throw new InsufficientFundsError();
}
// ISOLATION: другие транзакции не видят эти изменения
await trx('wallets').update({
balance: wallet.balance - amount,
});
await trx('orders').update({
status: 'paid',
paid_at: new Date(),
}).where('id', orderId);
// DURABILITY: при COMMIT все изменения безопасно записаны
return { success: true };
});
return transaction;
}
Когда ACID критичен?
- Финансовые системы — недопустимы потери денег
- Система заказов — нельзя потерять или задублировать заказ
- Критичные данные — где потеря данных = материальный ущерб
Для менее критичных операций (например, счётчики просмотров) можно использовать BASE (Basically Available, Soft state, Eventually consistent) вместо ACID для лучшей производительности.