Транзакции позволяют выполнять несколько SQL-запросов как единую атомарную операцию — либо все изменения применяются, либо ни одно из них. Вот как правильно работать с транзакциями.
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
// Начало транзакции
$pdo->beginTransaction();
// Выполнение операций
$stmt1 = $pdo->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?");
$stmt1->execute([100, 1]);
$stmt2 = $pdo->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?");
$stmt2->execute([100, 2]);
// Если все успешно — коммитим
$pdo->commit();
} catch (PDOException $e) {
// При ошибке — откатываем
$pdo->rollBack();
error_log("Transaction failed: " . $e->getMessage());
}
ACID-принципы:
Уровни изоляции (можно задать перед beginTransaction):
$pdo->exec("SET TRANSACTION ISOLATION LEVEL READ COMMITTED");
READ UNCOMMITTED
— грязное чтениеREAD COMMITTED
— предотвращает грязное чтениеREPEATABLE READ
— стандарт для InnoDBSERIALIZABLE
— максимальная изоляция$mysqli = new mysqli('localhost', 'user', 'password', 'test');
try {
// Отключаем autocommit
$mysqli->autocommit(false);
// Выполняем операции
$mysqli->query("UPDATE accounts SET balance = balance - 100 WHERE id = 1");
$mysqli->query("UPDATE accounts SET balance = balance + 100 WHERE id = 2");
// Проверяем на ошибки
if (!$mysqli->errno) {
$mysqli->commit();
} else {
throw new Exception($mysqli->error);
}
} catch (Exception $e) {
$mysqli->rollback();
error_log("Transaction failed: " . $e->getMessage());
} finally {
$mysqli->autocommit(true);
}
if ($stmt->rowCount() === 0) {
throw new Exception("No rows affected");
}
$pdo->beginTransaction();
try {
// Точка сохранения
$pdo->exec("SAVEPOINT part1");
$pdo->exec("INSERT INTO orders (...) VALUES (...)");
// Еще точка сохранения
$pdo->exec("SAVEPOINT part2");
$pdo->exec("UPDATE inventory SET quantity = quantity - 1");
$pdo->commit();
} catch (Exception $e) {
// Откат к последней точке сохранения
$pdo->exec("ROLLBACK TO SAVEPOINT part1");
// Альтернативная логика обработки
$pdo->exec("INSERT INTO failed_orders (...) VALUES (...)");
$pdo->commit();
}
DB::transaction(function () {
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();
// Если возникнет исключение — автоматический откат
});
для работы с транзакциями в PHP+MySQL используйте beginTransaction()
, commit()
и rollBack()
в PDO или autocommit(false)
, commit()
и rollback()
в MySQLi. Всегда обрабатывайте ошибки, проверяйте affected rows и держите транзакции максимально короткими. PDO предпочтительнее благодаря поддержке исключений и более чистому API.