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

Ответ

Транзакция — это группа SQL-запросов, которая выполняется как единая, атомарная операция. Она следует принципам ACID. Если все запросы выполняются успешно, изменения фиксируются (commit). Если возникает ошибка на любом этапе, все уже выполненные в рамках этой транзакции изменения откатываются (rollback), оставляя базу данных в согласованном состоянии.

Практический пример с PDO (перевод средств между счетами):

$pdo = new PDO('mysql:host=localhost;dbname=bank', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

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

    // Списываем сумму со счета отправителя
    $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]);

    // Фиксируем изменения, если все прошло успешно
    $pdo->commit();
    echo "Transfer successful";
} catch (PDOException $e) {
    // В случае любой ошибки откатываем все изменения
    $pdo->rollBack();
    echo "Transfer failed: " . $e->getMessage();
}

Ключевые моменты для PHP-разработчика:

  1. Изоляция через beginTransaction(): Запросы внутри транзакции изолированы от других соединений до момента commit() (уровень изоляции зависит от настройки СУБД).
  2. Обязательная обработка ошибок: Транзакции требуют использования try...catch для гарантированного вызова rollBack() при исключении.
  3. Поддержка драйверов: Транзакции работают с PDO для всех поддерживающих их СУБД (MySQL/InnoDB, PostgreSQL) и с расширением mysqli.
  4. Вложенные транзакции: PDO может эмулировать вложенные транзакции через SAVEPOINT, но реальная вложенность поддерживается на уровне самой СУБД (например, в PostgreSQL).

Ответ 18+ 🔞

А, слушай, про транзакции? Ну это ж, бля, основа основ, как не упасть в глубокую жопу при работе с деньгами или любыми другими важными данными. Представь себе: ты переводишь сто рублей с одного счёта на другой. Если просто выполнить два запроса подряд, может случиться пиздец. Первый запрос прошёл — деньги списались, а второй — наебнулся с ошибкой. И всё, сотня испарилась в никуда, клиент охуеет, а тебе потом искать, куда она подевалась, доверия ебать ноль.

Вот именно для этого и придумали транзакции. Это такая атомарная операция, ёпта. Либо всё выполняется успешно, либо нихуя не меняется. Как будто ты либо перенёс стакан, не пролив ни капли, либо даже не притронулся к нему. Всё держится на четырёх китах — принципах ACID, но если по-простому: операция надёжная, как швейцарские часы, и изолированная от другого говна, которое в это же время лезет в базу.

Смотри, как это выглядит в коде на PDO, на примере того самого перевода. Главное — обернуть всё в try...catch, иначе будет тебе хиросима.

$pdo = new PDO('mysql:host=localhost;dbname=bank', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

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

    // Списываем сумму со счета отправителя
    $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]);

    // Фиксируем изменения, если все прошло успешно
    $pdo->commit();
    echo "Transfer successful";
} catch (PDOException $e) {
    // В случае любой ошибки откатываем все изменения
    $pdo->rollBack();
    echo "Transfer failed: " . $e->getMessage();
}

Видишь, в чём соль? Сначала говоришь базе: «Так, бля, внимание, начинаем» — это beginTransaction(). Потом выполняешь свои манипуляции. Если на любом этапе, хоть на первом, хоть на втором запросе, вылезет ошибка — выполнение прыгнет в catch. И там команда rollBack() отменит ВСЁ, что уже успело выполниться в этой транзакции. Деньги волшебным образом вернутся на исходный счёт, как будто нихуя и не было. Если же всё прошло гладко, как по маслу — делаешь commit(), и изменения окончательно вбиваются в базу. Красота, да?

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

  1. Изоляция. Когда ты начал транзакцию, другие запросы могут не видеть твои «промежуточные» изменения, пока ты не сделал коммит. Это спасает от гонок и прочего мудацкого поведения. Уровень этой изоляции можно настраивать, но это уже тонкости.
  2. Обработка ошибок — ОБЯЗАТЕЛЬНА. Без try...catch ты рискуешь оставить транзакцию висящей в воздухе, а это, считай, овердохуища проблем. База будет держать блокировки, и всё начнёт тормозить и падать. Всегда лови исключения и делай откат.
  3. Драйверы. PDO — молодец, он умеет работать с транзакциями для нормальных движков баз, типа InnoDB в MySQL или PostgreSQL. С тем же MyISAM это, конечно, не прокатит, там транзакций нет от слова совсем.
  4. Вложенность. Тут интересно. PDO может прикинуться умным и сэмулировать вложенные транзакции через SAVEPOINT, если драйвер не поддерживает. Но по-настоящему это работает, например, в PostgreSQL. В общем, смотри по документации, чтобы не получить хуй в пальто вместо ожидаемого поведения.

Короче, запомни: любые операции, где важен целостный результат из нескольких шагов — переводы, оформление заказов, обновление связанных данных — это прям святая святых для транзакций. Без них — чих-пых тебя в сраку и потерянные данные.