Работа с транзакциями в MySQL и PHP: понятное руководство с примерами

Введение

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

В этой статье мы рассмотрим:

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

Когда и зачем использовать транзакции;

Как использовать транзакции в MySQL с PHP;

Примеры с PDO и MySQLi;

Возможные ошибки и отладка.

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

Транзакция — это логическая группа операций с базой данных, которая выполняется как единое целое. Транзакция должна соответствовать принципам ACID:

Atomicity (атомарность) — все или ничего.

Consistency (согласованность) — база остаётся в согласованном состоянии.

Isolation (изоляция) — параллельные транзакции не мешают друг другу.

Durability (надёжность) — изменения сохраняются даже при сбое.

Когда использовать транзакции?

Примеры:

Банковские переводы (списание со счёта А и зачисление на счёт B);

Заказы в интернет-магазине (добавление заказа и изменение остатков);

Резервирование мест (в отеле, самолёте и т. д.).

Без транзакций может случиться, что часть запроса выполнится, а другая — нет, и данные станут неконсистентными.

Поддержка транзакций в MySQL

> Транзакции поддерживаются только при использовании таблиц с типом InnoDB. Таблицы MyISAM не поддерживают транзакции.

Проверьте тип таблицы:

SHOW TABLE STATUS WHERE Name = ‘имя_таблицы’;

Если используется MyISAM, лучше сменить:

ALTER TABLE имя_таблицы ENGINE=InnoDB;

Основные команды транзакций

START TRANSACTION или BEGIN — начать транзакцию;

COMMIT — зафиксировать (сохранить) изменения;

ROLLBACK — откатить (отменить) изменения.

Использование транзакций в PHP

Вариант 1: С использованием PDO

<?php
try {
    $pdo = new PDO(«mysql:host=localhost;dbname=test_db», «user», «password»);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // Начинаем транзакцию
    $pdo->beginTransaction();

    // Первый запрос
    $pdo->exec(«INSERT INTO accounts (name, balance) VALUES (‘Иван’, 1000)»);

    // Второй запрос
    $pdo->exec(«INSERT INTO accounts (name, balance) VALUES (‘Петр’, 2000)»);

    // Если всё прошло успешно
    $pdo->commit();
    echo «Транзакция успешно завершена!»;
} catch (Exception $e) {
    // При ошибке — откат
    $pdo->rollBack();
    echo «Ошибка транзакции: » . $e->getMessage();
}
?>

Вариант 2: С использованием MySQLi

<?php
$mysqli = new mysqli(«localhost», «user», «password», «test_db»);

if ($mysqli->connect_errno) {
    die(«Ошибка подключения: » . $mysqli->connect_error);
}

$mysqli->begin_transaction();

try {
    $mysqli->query(«INSERT INTO accounts (name, balance) VALUES (‘Саша’, 1500)»);
    $mysqli->query(«INSERT INTO accounts (name, balance) VALUES (‘Маша’, 2500)»);

    $mysqli->commit();
    echo «Транзакция успешно завершена!»;
} catch (Exception $e) {
    $mysqli->rollback();
    echo «Ошибка транзакции: » . $e->getMessage();
}
?>

Что произойдёт при ошибке?

Если один из запросов завершится ошибкой (например, нарушена уникальность), то:

Выбросится исключение (если включён режим ошибок);

PHP выполнит ROLLBACK (откат);

Все изменения в БД, сделанные в этой транзакции, будут отменены.

Важно: следите за тем, чтобы ошибки действительно выбрасывались. У PDO должен быть установлен PDO::ERRMODE_EXCEPTION, а у MySQLi желательно использовать try…catch.

Пример: Перевод денег с одного счёта на другой

<?php
$pdo = new PDO(«mysql:host=localhost;dbname=bank», «user», «pass»);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$fromId = 1;
$toId = 2;
$amount = 500;

try {
    $pdo->beginTransaction();

    // Списываем деньги
    $stmt = $pdo->prepare(«UPDATE accounts SET balance = balance — ? WHERE id = ?»);
    $stmt->execute([$amount, $fromId]);

    // Проверка на отрицательный баланс
    $stmt = $pdo->prepare(«SELECT balance FROM accounts WHERE id = ?»);
    $stmt->execute([$fromId]);
    $balance = $stmt->fetchColumn();
    if ($balance < 0) {
        throw new Exception(«Недостаточно средств»);
    }

    // Зачисляем деньги
    $stmt = $pdo->prepare(«UPDATE accounts SET balance = balance + ? WHERE id = ?»);
    $stmt->execute([$amount, $toId]);

    $pdo->commit();
    echo «Перевод успешно выполнен»;
} catch (Exception $e) {
    $pdo->rollBack();
    echo «Ошибка перевода: » . $e->getMessage();
}
?>

Дополнительные советы

Всегда проверяйте, поддерживает ли таблица транзакции (InnoDB);

Используйте исключения (try…catch) для управления откатом;

Избегайте длительных транзакций, чтобы не блокировать таблицы;

Не смешивайте транзакционные и нетранзакционные таблицы в одной операции;

Логируйте ошибки и действия, особенно в критических системах.

Заключение

Транзакции — мощный инструмент, который защищает данные от потери и порчи. При правильном использовании вы получите стабильную и надёжную систему, в которой ошибки не приведут к катастрофе. Используйте PDO или MySQLi с транзакциями в PHP и обеспечьте максимальную целостность ваших данных.

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Сайт создал Романенко Артем