Транзакции и автоматическая фиксация изменений

После подключения к базе данных средствами модуля PDO потребуется понять, как PDO управляет транзакциями, прежде чем отправлять запросы. Тем, кто прежде не сталкивались с транзакциями, полезно знать 4 главных характеристики транзакций: атомарность, согласованность, изолированность и долговечность (англ. Atomicity, Consistency, Isolation and Durability, или ACID). Говоря простым языком, транзакция гарантирует, что каждая операция с базой данных выполнится безопасно и без помех со стороны других подключений, даже если операция выполняется поэтапно. Транзакционные операции автоматически отменяются по запросу, если транзакцию ещё не зафиксировали, что упрощает обработку ошибок в скриптах.

Работа механизма транзакций часто состоит в «накоплении» пакета изменений, которые затем выполняются как одно целое; приятный побочный эффект такой работы состоит в резком увеличении эффективности этих обновлений. Другими словами, транзакции ускоряют скрипты и потенциально повышают их надёжность, хотя для этого и потребуется правильно работать с транзакциями, чтобы получить эти преимущества.

Не каждая база данных поддерживает транзакции, поэтому при первом подключении модуль PDO вынужден работать в так называемом режиме «автоматической фиксации». В режиме автофиксации модуль оборачивает каждый запрос к базе данных в неявную транзакцию, если СУБД поддерживает транзакции, или выполняет отдельные запросы, если база данных не поддерживает механизм транзакций. Явное начало транзакции обозначают вызовом метода PDO::beginTransaction(). Независимо от настроек обработки ошибок модуль выбросит исключение PDOException, если нижележащий драйвер не поддерживает механизм транзакций, поскольку попытка начать транзакцию без поддержки драйвера — серьёзная ошибка. Внутри транзакции изменения фиксируют методом PDO::commit(), если код выполнился успешно, или откатывают транзакцию методом PDO::rollBack(), если при запуске кода в течение транзакции возникла ошибка.

Внимание

Модуль PDO проверяет доступность транзакций только на уровне драйвера. Метод PDO::beginTransaction() вернёт true без ошибок, если сервер базы данных примет запрос на запуск транзакции, даже если в конкретных условиях при выполнении запроса выяснится, что транзакции недоступны.

К таким примерам относится попытка запуска транзакций в таблицах MyISAM базы данных MySQL.

Внимание

Неявные фиксации при выполнении DDL-запросов: Отдельные базы данных неявно выполняют команду COMMIT и фиксируют изменения в одной транзакции при выполнении серии DDL-запросов (англ. Database Definition Language) наподобие DROP TABLE или CREATE TABLE. Поэтому изменения, которые оказались в транзакции, автоматически фиксируются, и откатить такие изменения невозможно.

К примерам баз данных с таким поведением относятся СУБД MySQL и Oracle.

Пример #1 Пример неявной фиксации

<?php

$pdo
->beginTransaction();
$pdo->exec("INSERT INTO users (name) VALUES ('Rasmus')");
$pdo->exec("CREATE TABLE test (id INT PRIMARY KEY)"); // Неявная фиксация командой COMMIT выполняется в этом месте
$pdo->rollBack(); // Невозможно отменить серию команд INSERT/CREATE в БД MySQL или Oracle

?>

Лучшая практика: При работе с базами данных с таким поведением рекомендуют избегать выполнения DDL-запросов внутри транзакций. DDL-операции исключают из транзакционной логики, когда требуется.

Модуль PDO автоматически откатит невыполненные транзакции при завершении работы скрипта или при закрытии соединения. Откат транзакций — мера безопасности, которая помогает избегать нарушения целостности данных, когда скрипт неожиданно прерывает работу. Модуль предполагает, что взаимодействие с базой данных нарушилось, раз изменения не зафиксировали явным образом, и откатывает изменения для безопасности данных.

Внимание

Изменения откатятся автоматически, только если транзакцию открыли методом PDO::beginTransaction(). При ручной отправке запроса, который начинает транзакцию, PDO не узнает об этом, поэтому не откатит транзакцию при сбое.

Пример #2 Выполнение пакета изменений в рамках транзакции

В следующем примере предположим, что требуется создать набор записей для нового сотрудника с идентификатором 23. Кроме ввода базовой информации о сотруднике потребуется записать его зарплату. Вместо двух отдельных изменений обернём обновление таблиц методами PDO::beginTransaction() и PDO::commit(), чтобы гарантировать, что никто другой не увидит этих изменений, пока вставка данных не завершится. При сбое блок catch откатит изменения, которые внесли с момента начала транзакции, и выведет сообщение об ошибке.

<?php

try {
$dbh = new PDO(
'odbc:SAMPLE',
'db2inst1',
'ibmdb2',
array(
PDO::ATTR_PERSISTENT => true)
);

echo
"Подключились\n";
} catch (
Exception $e) {
die(
"Возникла ошибка подключения: " . $e->getMessage());
}

try {
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$dbh->beginTransaction();
$dbh->exec("INSERT INTO staff (id, first, last) VALUES (23, 'Joe', 'Bloggs')");
$dbh->exec("INSERT INTO salarychange (id, amount, changedate) VALUES (23, 50000, NOW())");
$dbh->commit();
} catch (
Exception $e) {
$dbh->rollBack();
echo
"Ошибка: " . $e->getMessage();
}

?>

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

Добавить

Примечания пользователей 3 notes

up
6
hooby404 at gmail dot com
2 years ago
> You're not limited to making updates in a transaction; you can also issue complex
> queries to extract data, and possibly use that information to build up more updates
> and queries; while the transaction is active, you are guaranteed that no one else can
> make changes while you are in the middle of your work. For further reading on
> transactions, refer to the documentation provided by your database server.

This only holds true if you specifically do "SELECT .... FOR UPDATE".

Without the "FOR UPDATE" part, when two transactions run at the same time, the second transaction could change a value AFTER the first transaction read it, but BEFORE the first transaction used it for updates.

Without the "FOR UPDATE" part you are absolutely NOT GUARANTEED that no one else can make changes while you are in the middle of your work.
up
5
harl at gmail dot com
7 years ago
Some DBMSs allow DDL (table creation/alteration) within transactions, some do not. Asking "Does my DBMS allow DDL within transactions without forcing a commit?" gives the following example answers:

CUBRID: Yes
DB2 UDB: Yes
Firebird: Yes
Informix: Yes
MySQL: No
Oracle: No (although schema upgrades can be rolled out using "edition-based redefinition")
PostgreSQL: Yes
SQLite: Yes
SQL Server: Sometimes, depending on isolation level, type of command, etc.
Sybase: Yes
up
4
pasamio at gmail dot com
12 years ago
Typically data definition language clauses (DDL) will trigger the database engine to automatically commit:
http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html

Many other databases (e.g. Oracle) will implicitly commit before and after running DDL statements.
To Top