
Введение
Когда вы работаете с базой данных, особенно в финансовых или других критичных системах, важно убедиться, что все изменения выполняются корректно и полностью. Для этого используются транзакции. Транзакции позволяют сгруппировать несколько 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 и обеспечьте максимальную целостность ваших данных.