Я читал онлайн-руководство по php, но я все еще не уверен в том, как работают эти две функции: mysqli :: commit & mysqli :: rollback.
Первое, что я должен сделать, это:
$mysqli->autocommit(FALSE);
Затем я делаю несколько запросов:
$mysqli->query("..."); $mysqli->query("..."); $mysqli->query("...");
Затем я совершаю транзакцию, состоящую из этих 3 запросов, делая:
$mysqli->commit();
НО в неудачном случае, когда один из этих запросов не работает, все 3 запроса отменяются или мне приходится называть откат самостоятельно? Я хочу, чтобы все 3 запроса были атомарными и считались только одним запросом. Если один запрос терпит неудачу, все 3 должны потерпеть неудачу и не будут иметь никакого эффекта.
Я спрашиваю об этом, потому что в комментариях, которые я видел на странице руководства: http://php.net/manual/en/mysqli.commit.php пользователь Lorenzo вызывает откат, если один из запросов не удался.
Что такое откат, если 3 запроса являются атомарными? Я не понимаю.
EDIT: Это пример кода, в котором я сомневаюсь:
<?php $all_query_ok=true; // our control variable $mysqli->autocommit(false); //we make 4 inserts, the last one generates an error //if at least one query returns an error we change our control variable $mysqli->query("INSERT INTO myCity (id) VALUES (100)") ? null : $all_query_ok=false; $mysqli->query("INSERT INTO myCity (id) VALUES (200)") ? null : $all_query_ok=false; $mysqli->query("INSERT INTO myCity (id) VALUES (300)") ? null : $all_query_ok=false; $mysqli->query("INSERT INTO myCity (id) VALUES (100)") ? null : $all_query_ok=false; //duplicated PRIMARY KEY VALUE //now let's test our control variable $all_query_ok ? $mysqli->commit() : $mysqli->rollback(); $mysqli->close(); ?>
Я думаю, что этот код неверен, потому что если какой-либо из запросов не удалось и $all_query_ok==false
вам не нужно делать откаты, потому что транзакция не обрабатывалась. Я прав?
Я думаю, что этот код неверен, потому что если какой-либо из запросов не удалось и $ all_query_ok == false, вам не нужно делать откаты, потому что транзакция не обрабатывалась. Я прав?
Нет, транзакция не отслеживается при сбое одного SQL-Statement.
Если какой-либо SQL-Statement не работает, оператор откатывается (как описано в ответе @ eggyal), но транзакция по-прежнему остается открытой. Если вы теперь вызываете commit
, откат успешных утверждений не выполняется, и вы просто вставляете «поврежденные» данные в свою базу данных. Вы можете легко воспроизвести это:
m> CREATE TABLE transtest (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL DEFAULT '', CONSTRAINT UNIQUE KEY `uq_transtest_name` (name)) ENGINE=InnoDB; Query OK, 0 rows affected (0.07 sec) m> START TRANSACTION; Query OK, 0 rows affected (0.00 sec) m> INSERT INTO transtest (name) VALUE ('foo'); Query OK, 1 row affected (0.00 sec) m> INSERT INTO transtest (name) VALUE ('foo'); ERROR 1062 (23000): Duplicate entry 'foo' for key 'uq_transtest_name' m> INSERT INTO transtest (name) VALUE ('bar'); Query OK, 1 row affected (0.00 sec) m> COMMIT; Query OK, 0 rows affected (0.02 sec) m> SELECT * FROM transtest; +----+------+ | id | name | +----+------+ | 3 | bar | | 1 | foo | +----+------+ 2 rows in set (0.00 sec)
Вы видите, что вставка «foo» и «bar» прошла успешно, хотя второй SQL-оператор не удался – вы даже можете увидеть, что AUTO_INCREMENT
было увеличено по ошибочному запросу.
Таким образом, вы должны проверять результаты каждого query
-call, и если один из них не работает, откатите вызов, чтобы отменить другие успешные запросы. Поэтому код Лоренцо в PHP-руководстве имеет смысл.
Единственная ошибка, которая заставляет MySQL откатывать транзакцию, – это «тупик транзакции» (и это специфично для InnoDB, другие механизмы хранения могут обрабатывать эти ошибки по-разному).
Как InnoDB
разделе «Обработка ошибок InnoDB
:
Обработка ошибок в
InnoDB
не всегда такая же, как в стандарте SQL. Согласно стандарту, любая ошибка во время SQL-запроса должна приводить к откату этого оператора. ИногдаInnoDB
откатывает только часть отчета или всю транзакцию. Следующие пункты описывают, какInnoDB
выполняет обработку ошибок:
Если у вас закончилось файловое пространство в табличном пространстве , возникает столбец MySQL
Table is full
иInnoDB
откатывается от оператора SQL.Сбой транзакции заставляет
InnoDB
откатывать всю транзакцию . Повторяйте всю транзакцию, когда это произойдет.Тайм-аут ожидания блокировки заставляет
InnoDB
откатывать только один оператор, ожидающий блокировки, и столкнулся с таймаутом. (Чтобы вернуть всю транзакцию транзакции, запустите сервер с параметром--innodb_rollback_on_timeout
.) Повторите инструкцию, если используете текущее поведение, или всю транзакцию при использовании--innodb_rollback_on_timeout
.Оба взаимоблокировки и тайм-ауты ожидания ожидания являются нормальными на занятых серверах, и для приложений необходимо знать, что они могут произойти и обрабатывать их путем повторной попытки. Вы можете сделать их менее вероятными, выполнив как можно меньше работы между первым изменением данных во время транзакции и фиксацией, поэтому блокировки удерживаются в течение как можно более короткого времени и минимально возможного количества строк. Иногда разделение между различными транзакциями может быть практичным и полезным.
Когда откат транзакции происходит из-за тайм-аута блокировки или блокировки ожидания, он отменяет действие операторов внутри транзакции. Но если в заявлении о начале транзакции были инструкции
START TRANSACTION
илиBEGIN
, откат не отменяет эту инструкцию. Другие операторы SQL становятся частью транзакции до появленияCOMMIT
,ROLLBACK
или некоторого оператора SQL, который вызывает неявное коммитирование.Ошибка дублирующегося ключа возвращает запрос SQL, если вы не указали опцию
IGNORE
в своем заявлении.
row too long error
возвращает запрос SQL.Другие ошибки в основном обнаруживаются слоем кода MySQL (выше уровня ядра хранилища
InnoDB
), и они откатывают соответствующий SQL-запрос. Замки не освобождаются при откате одного оператора SQL.