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

Как устроена транзакция?

2.3 Middle🔥 242 комментариев
#Базы данных и SQL

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

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

Что такое транзакция в базах данных?

В контексте PHP Backend разработки транзакция — это логическая единица работы с базой данных, которая рассматривается как единое целое. Её основная цель — обеспечить целостность данных при выполнении последовательности SQL-операций, даже в условиях сбоев или параллельного доступа. Транзакция либо выполняется полностью (commit), либо не выполняется вовсе (rollback).

Свойства транзакций (ACID)

Транзакции должны соответствовать четырём ключевым свойствам, известным как ACID:

  • Атомарность (Atomicity): Транзакция — это "всё или ничего". Все операции внутри неё либо выполняются успешно, либо ни одна из них не применяется к базе данных.
  • Согласованность (Consistency): Транзакция переводит базу данных из одного целостного состояния в другое. Все бизнес-правила и ограничения (constraints) соблюдаются.
  • Изолированность (Isolation): Параллельно выполняющиеся транзакции не должны влиять друг на друга. Их промежуточные результаты не видны другим транзакциям до фиксации.
  • Долговечность (Durability): После успешной фиксации (commit) результаты транзакции сохраняются в базе данных постоянно, даже в случае последующего сбоя системы.

Механизм работы транзакции

Работа с транзакцией в PHP (например, с использованием PDO или mysqli) следует чёткому паттерну:

<?php
// Пример с PDO
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
    // 1. Начало транзакции
    $pdo->beginTransaction();

    // 2. Выполнение операций внутри транзакции
    $stmt1 = $pdo->prepare("UPDATE accounts SET balance = balance - :amount WHERE id = :from_id");
    $stmt1->execute([':amount' => 100, ':from_id' => 1]);

    $stmt2 = $pdo->prepare("UPDATE accounts SET balance = balance + :amount WHERE id = :to_id");
    $stmt2->execute([':amount' => 100, ':to_id' => 2]);

    // 3. Фиксация изменений (при успехе)
    $pdo->commit();
    echo "Транзакция успешно завершена.";

} catch (Exception $e) {
    // 4. Откат изменений (при ошибке)
    $pdo->rollBack();
    echo "Ошибка транзакции: " . $e->getMessage();
}

Подробные этапы:

  1. Старт (BEGIN TRANSACTION / beginTransaction()). Система управления базами данных (СУБД) отмечает точку начала. Все последующие операции временно изолируются.
  2. Выполнение операций. Выполняются запросы INSERT, UPDATE, DELETE, SELECT ... FOR UPDATE. Изменения на этом этапе:
    *   Видимы **внутри** текущей транзакции.
    *   **Не видны** другим транзакциям (зависит от уровня изоляции).
    *   Ещё **не записаны** окончательно в базу, а находятся в буфере.
  1. Фиксация (COMMIT). Это критическая точка. СУБД:
    *   Проверяет целостность данных (ограничения, внешние ключи).
    *   Окончательно записывает все изменения из буфера на диск.
    *   Освобождает все блокировки (lock), установленные в ходе транзакции.
    *   Делает изменения видимыми для других транзакций.
  1. Откат (ROLLBACK). Если на любом этапе возникает ошибка (исключение в коде, нарушение ограничения БД, deadlock), происходит:
    *   Полная отмена всех операций этой транзакции.
    *   Возврат базы данных к состоянию на момент `BEGIN`.
    *   Освобождение блокировок.

Уровни изоляции транзакций

Стандарт SQL определяет уровни изоляции, которые балансируют между согласованностью данных и производительностью. В MySQL/InnoDB они настраиваются:

-- Установка уровня изоляции
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
  • READ UNCOMMITTED: Минимальная изоляция. Возможны "грязные" чтения (видны незафиксированные данные других транзакций).
  • READ COMMITTED: Гарантирует, что чтение видит только зафиксированные данные. Но возможно "неповторяющееся чтение" (значение строки меняется при повторном чтении внутри той же транзакции).
  • REPEATABLE READ (дефолт в InnoDB): Гарантирует, что данные, прочитанные один раз, не изменятся при повторном чтении в рамках той же транзакции. Защищает от "грязных" и "неповторяющихся" чтений.
  • SERIALIZABLE: Максимальная изоляция. Транзакции выполняются так, как если бы они шли строго последовательно. Полностью исключает аномалии, но сильно снижает параллельную производительность.

Практические аспекты для Backend-разработчика

  • Короткие транзакции: Старайтесь выполнять транзакции как можно быстрее, не делая внутри них сетевые запросы или тяжелые вычисления. Это уменьшает время блокировок и вероятность deadlock.
  • Обработка ошибок: Всегда оборачивайте транзакцию в try-catch блок. При использовании autocommit = false обязательно делайте явный commit или rollback.
  • Взаимодействие с ORM (Doctrine, Eloquent): Современные ORM предоставляют абстракции над транзакциями (Unit of Work), но принцип ACID сохраняется. Понимание, когда ORM открывает/закрывает транзакции, критически важно.
  • Распределённые транзакции: В микросервисной архитектуре, где данные находятся в разных БД или сервисах, стандартные транзакции БД не работают. Здесь применяются паттерны Saga или используются механизмы идемпотентности и компенсирующих операций.

Таким образом, транзакция — это фундаментальный механизм, обеспечивающий надежность и предсказуемость работы с данными. Её грамотное использование отличает зрелый backend-сервис, особенно в финансовых системах, где важна каждая операция.