← Назад к вопросам

Что такое Транзакция?

1.0 Junior🔥 161 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Транзакция в базах данных

Транзакция — это набор операций БД, которые либо все выполнятся успешно, либо все откатятся. Это гарантирует консистентность данных.

ACID свойства

Любая транзакция должна соответствовать ACID:

Atomicity (Атомарность)

  • Либо все операции выполнены, либо ни одна
  • Нет промежуточных состояний
// Перевод денег со счёта A на счёт B
BEGIN TRANSACTION;
  UPDATE accounts SET balance = balance - 100 WHERE id = 1; // A
  UPDATE accounts SET balance = balance + 100 WHERE id = 2; // B
COMMIT; // Оба выполнились

// Если ошибка между UPDATE:
BEGIN TRANSACTION;
  UPDATE accounts SET balance = balance - 100 WHERE id = 1;
  -- Ошибка!
  UPDATE accounts SET balance = balance + 100 WHERE id = 2;
ROLLBACK; // Оба откатились, A вернул деньги

Consistency (Консистентность)

  • БД переходит из одного консистентного состояния в другое
  • Все business rules соблюдаются
// Constraint: сумма на счётах не может быть отрицательной
BEGIN TRANSACTION;
  UPDATE accounts SET balance = -50 WHERE id = 1; // Нарушает constraint
ROLLBACK; // Откатится, потому что нарушает консистентность

Isolation (Изоляция)

  • Параллельные транзакции не мешают друг другу
  • Каждая видит консистентную версию данных
// Транзакция 1: читает баланс = 1000
// Транзакция 2: вычитает 100 (баланс становится 900)
// Транзакция 1: видит 1000 (изолирована)

Durability (Долговечность)

  • После коммита данные сохранены
  • Даже при сбое сервера данные не потеряются
BEGIN TRANSACTION;
  UPDATE accounts SET balance = 900 WHERE id = 1;
COMMIT; // Данные записаны на диск
// Сервер падает — данные в безопасности

Примеры в Node.js

TypeORM (QueryRunner)

const queryRunner = dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();

try {
  // Операции в транзакции
  const account1 = await queryRunner.manager.findOne(Account, { where: { id: 1 } });
  const account2 = await queryRunner.manager.findOne(Account, { where: { id: 2 } });
  
  account1.balance -= 100;
  account2.balance += 100;
  
  await queryRunner.manager.save(account1);
  await queryRunner.manager.save(account2);
  
  await queryRunner.commitTransaction();
} catch (err) {
  await queryRunner.rollbackTransaction();
  throw err;
} finally {
  await queryRunner.release();
}

Sequelize

const transaction = await sequelize.transaction();

try {
  const account1 = await Account.findByPk(1, { transaction });
  const account2 = await Account.findByPk(2, { transaction });
  
  await account1.update({ balance: account1.balance - 100 }, { transaction });
  await account2.update({ balance: account2.balance + 100 }, { transaction });
  
  await transaction.commit();
} catch (err) {
  await transaction.rollback();
  throw err;
}

Raw SQL

const connection = await pool.connect();

try {
  await connection.query('BEGIN');
  
  await connection.query(
    'UPDATE accounts SET balance = balance - $1 WHERE id = $2',
    [100, 1]
  );
  
  await connection.query(
    'UPDATE accounts SET balance = balance + $1 WHERE id = $2',
    [100, 2]
  );
  
  await connection.query('COMMIT');
} catch (err) {
  await connection.query('ROLLBACK');
  throw err;
} finally {
  connection.release();
}

Уровни изоляции

Read Uncommitted (грязное чтение возможно)

Транзакция 1: UPDATE balance = 900
Транзакция 2: читает 900 (до коммита Т1)
Транзакция 1: ROLLBACK → баланс снова 1000
Транзакция 2: видела несуществующее значение

Read Committed (по умолчанию в PostgreSQL)

Транзакция 1: UPDATE balance = 900
Транзакция 2: ждёт коммита Т1, потом читает 900

Repeatable Read (PostgreSQL по умолчанию для transactions)

Транзакция 1: читает баланс = 1000
Транзакция 2: UPDATE balance = 900, COMMIT
Транзакция 1: повторно читает — всё ещё видит 1000 (снимок)

Serializable (максимальная изоляция)

Транзакции выполняются как будто по очереди
Медленнее, но самое безопасное

Проблемы

Deadlock

Транзакция 1: заблокировала строку A, ждёт строку B
Транзакция 2: заблокировала строку B, ждёт строку A
→ Deadlock! Одна откатится

Lost Update

Транзакция 1: читает balance = 1000
Транзакция 2: читает balance = 1000
Транзакция 1: пишет balance = 900, коммит
Транзакция 2: пишет balance = 950, коммит
→ потеряется обновление Т1

Phantom Read

Транзакция 1: SELECT * WHERE age > 18 → 5 rows
Транзакция 2: INSERT new user with age > 18, COMMIT
Транзакция 1: SELECT * WHERE age > 18 → 6 rows (фантом!)

Практические советы

✅ Делай транзакции короткими ✅ Избегай блокировок больших таблиц ✅ Используй appropriate isolation level ✅ Обрабатывай ошибки и rollback ✅ Логируй проблемы для анализа

❌ Не оставляй транзакции открытыми ❌ Не делай долгих операций в транзакции ❌ Не забывай про error handling

Что такое Транзакция? | PrepBro