Я читал онлайн-руководство по 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.