Mysql transactions with php
Поддержка транзакций в СУБД MySQL зависит от используемого движка хранилища данных. Начиная с MySQL 5.5, по умолчанию используется движок InnoDB. InnoDB полностью поддерживает модель транзакций ACID.
Транзакциями можно управлять как средствами SQL, так и вызовами API-функций. Для включения и выключения режима автофиксации изменений ( autocommit ) рекомендуется пользоваться API функциями.
Пример #1 Установка режима автофиксации ( autocommit ) средствами SQL и функциями API
mysqli_report ( MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT );
$mysqli = new mysqli ( «example.com» , «user» , «password» , «database» );
?php
/* Рекомендуется управлять настройками транзакций средствами API */
$mysqli -> autocommit ( false );
/* Не будет распознаваться и учитываться плагинами репликации и балансировки нагрузки */
$mysqli -> query ( ‘SET AUTOCOMMIT = 0’ );
Дополнительные службы сервера, такие как плагины репликации и балансировки нагрузки, могут отслеживать вызовы API-функций. Плагин репликации может сообщать балансировщику нагрузки о запущенной транзакции, если эта транзакция обслуживается API-функциями. Сервер не сможет распределять нагрузку между репликами базы, если смена режима автофиксации ( autocommit ), фиксация и откат транзакций осуществляются SQL-запросами.
Пример #2 Фиксация и откат
mysqli_report ( MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT );
$mysqli = new mysqli ( «example.com» , «user» , «password» , «database» );
$mysqli -> autocommit ( false );
?php
$mysqli -> query ( «INSERT INTO test(id) VALUES (1)» );
$mysqli -> rollback ();
$mysqli -> query ( «INSERT INTO test(id) VALUES (2)» );
$mysqli -> commit ();
Следует заметить, что сервер MySQL не может откатить результаты всех запросов. Некоторые изменения фиксируются неявно.
User Contributed Notes
Transactions and auto-commit
Now that you’re connected via PDO, you must understand how PDO manages transactions before you start issuing queries. If you’ve never encountered transactions before, they offer 4 major features: Atomicity, Consistency, Isolation and Durability (ACID). In layman’s terms, any work carried out in a transaction, even if it is carried out in stages, is guaranteed to be applied to the database safely, and without interference from other connections, when it is committed. Transactional work can also be automatically undone at your request (provided you haven’t already committed it), which makes error handling in your scripts easier.
Transactions are typically implemented by «saving-up» your batch of changes to be applied all at once; this has the nice side effect of drastically improving the efficiency of those updates. In other words, transactions can make your scripts faster and potentially more robust (you still need to use them correctly to reap that benefit).
Unfortunately, not every database supports transactions, so PDO needs to run in what is known as «auto-commit» mode when you first open the connection. Auto-commit mode means that every query that you run has its own implicit transaction, if the database supports it, or no transaction if the database doesn’t support transactions. If you need a transaction, you must use the PDO::beginTransaction() method to initiate one. If the underlying driver does not support transactions, a PDOException will be thrown (regardless of your error handling settings: this is always a serious error condition). Once you are in a transaction, you may use PDO::commit() or PDO::rollBack() to finish it, depending on the success of the code you run during the transaction.
PDO only checks for transaction capabilities on driver level. If certain runtime conditions mean that transactions are unavailable, PDO::beginTransaction() will still return true without error if the database server accepts the request to start a transaction.
An example of this would be trying to use transactions on MyISAM tables on a MySQL database.
When the script ends or when a connection is about to be closed, if you have an outstanding transaction, PDO will automatically roll it back. This is a safety measure to help avoid inconsistency in the cases where the script terminates unexpectedly—if you didn’t explicitly commit the transaction, then it is assumed that something went awry, so the rollback is performed for the safety of your data.
The automatic rollback only happens if you initiate the transaction via PDO::beginTransaction() . If you manually issue a query that begins a transaction PDO has no way of knowing about it and thus cannot roll it back if something bad happens.
Example #1 Executing a batch in a transaction
In the following sample, let’s assume that we are creating a set of entries for a new employee, who has been assigned an ID number of 23. In addition to entering the basic data for that person, we also need to record their salary. It’s pretty simple to make two separate updates, but by enclosing them within the PDO::beginTransaction() and PDO::commit() calls, we are guaranteeing that no one else will be able to see those changes until they are complete. If something goes wrong, the catch block rolls back all changes made since the transaction was started, and then prints out an error message.
try $dbh = new PDO ( ‘odbc:SAMPLE’ , ‘db2inst1’ , ‘ibmdb2’ ,
array( PDO :: ATTR_PERSISTENT => true ));
echo «Connected\n» ;
> catch ( Exception $e ) die( «Unable to connect: » . $e -> getMessage ());
>
?php
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 «Failed: » . $e -> getMessage ();
>
?>
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.
mysqli_begin_transaction
Стартует транзакцию. Требуется InnoDB (разрешён по умолчанию). Для дополнительной информации, как работают транзакции в MySQL, читайте » http://dev.mysql.com/doc/mysql/en/commit.html.
Список параметров
Только для процедурного стиля: объект mysqli , полученный с помощью mysqli_connect() или mysqli_init() .
- MYSQLI_TRANS_START_READ_ONLY : Стартовать транзакцию как «START TRANSACTION READ ONLY». Требуется MySQL 5.6 или выше.
- MYSQLI_TRANS_START_READ_WRITE : Стартовать транзакцию как «START TRANSACTION READ WRITE». Требуется MySQL 5.6 или выше.
- MYSQLI_TRANS_START_WITH_CONSISTENT_SNAPSHOT : Стартовать транзакцию как «START TRANSACTION WITH CONSISTENT SNAPSHOT».
Точка сохранения транзакции.
Возвращаемые значения
Возвращает true в случае успешного выполнения или false в случае возникновения ошибки.
Список изменений
Примеры
Пример #1 Пример использования mysqli::begin_transaction()
/* Указать mysqli выбрасывать исключение в случае возникновения ошибки */
mysqli_report ( MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT );
$mysqli = new mysqli ( «localhost» , «my_user» , «my_password» , «world» );
/* Движок таблиц должен поддерживать транзакции */
$mysqli -> query ( «CREATE TABLE IF NOT EXISTS language (
Code text NOT NULL,
Speakers int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;» );
/* Начало транзакции */
$mysqli -> begin_transaction ();
try /* Добавление каких-то значений */
$mysqli -> query ( «INSERT INTO language(Code, Speakers) VALUES (‘DE’, 42000123)» );
/* Попытка добавить недопустимые значения */
$language_code = ‘FR’ ;
$native_speakers = ‘Unknown’ ;
$stmt = $mysqli -> prepare ( ‘INSERT INTO language(Code, Speakers) VALUES (. )’ );
$stmt -> bind_param ( ‘ss’ , $language_code , $native_speakers );
$stmt -> execute ();
/* Если код достигает этой точки без ошибок, фиксируем данные в базе данных. */
$mysqli -> commit ();
> catch ( mysqli_sql_exception $exception ) $mysqli -> rollback ();
/* Указать mysqli выбрасывать исключение в случае возникновения ошибки */
mysqli_report ( MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT );
$mysqli = mysqli_connect ( «localhost» , «my_user» , «my_password» , «world» );
/* Движок таблиц должен поддерживать транзакции */
mysqli_query ( $mysqli , «CREATE TABLE IF NOT EXISTS language (
Code text NOT NULL,
Speakers int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;» );
/* Начало транзакции */
mysqli_begin_transaction ( $mysqli );
try /* Добавление каких-то значений */
mysqli_query ( $mysqli , «INSERT INTO language(Code, Speakers) VALUES (‘DE’, 42000123)» );
/* Попытка добавить недопустимые значения */
$language_code = ‘FR’ ;
$native_speakers = ‘Unknown’ ;
$stmt = mysqli_prepare ( $mysqli , ‘INSERT INTO language(Code, Speakers) VALUES (. )’ );
mysqli_stmt_bind_param ( $stmt , ‘ss’ , $language_code , $native_speakers );
mysqli_stmt_execute ( $stmt );
/* Если код достигает этой точки без ошибок, фиксируем данные в базе данных. */
mysqli_commit ( $mysqli );
> catch ( mysqli_sql_exception $exception ) mysqli_rollback ( $mysqli );
Примечания
Замечание:
Функция не работает с нетранзакционными типами таблиц (такими как MyISAM или ISAM).
Смотрите также
- mysqli_autocommit() — Включает или отключает автоматическую фиксацию изменений базы данных
- mysqli_commit() — Фиксирует текущую транзакцию
- mysqli_rollback() — Откат текущей транзакции
User Contributed Notes 4 notes
If you receive errors like: «This server version doesn’t support ‘READ WRITE’ and ‘READ ONLY’. Minimum 5.6.5 is required» with versions of MariaDB that DO support them, this is due to an internal check in mysqli conflicting with a hack in MariaDB to allow replication with oracle mysql.
MariaDB prefixes its server version numbers with «5.5.5-» for example «5.5.5-10.3.7-MariaDB-1:10.3.7+maria~stretch». This is because oracle mysql would interpet the «10» as version 1. Mysql clients aware of MariaDB have been updated to detect and strip this prefix.
However the check for mysqli.begin-transaction sees the 5.5.5 prefix and so fails.
The workaround is to specify a custom version string without the prefix for MariaDB on the command line using the —version option. Then mysqli.begin-transaction functions as expected.
MySQL 5.6 introduces READ ONLY mode which applies optimizations to your transactions that can only be applied when it knows in advance that no table modifications will be made and that no locks will be issued.
The default access mode is READ WRITE in all versions up to and including MySQL 5.6. Starting in MySQL 5.7, the appropriate access mode is detected automatically. So if your transaction attempts modifications or table locks, it will automatically use READ WRITE mode, otherwise it will use READ ONLY mode and your transaction will benefit from the optimizations that come from that without having to explicitly declare is as READ ONLY.
Therefore the only time you need to explicitly declare an access mode is when you are using MySQL 5.6 and you are sure that you want READ ONLY mode. Note that any queries that attempt to modify tables or issue locks in READ ONLY mode will fail. Temporary tables can still be modified.
(Moderators. This post should replace the previous post that I made on the subject. Thanks.)
The above answer from Ral worked for us, Thanks a lot. This is how we implemented the proposed workaround for
Warning: mysqli_begin_transaction(): This server version doesn’t support ‘READ WRITE’ and ‘READ ONLY’. Minimum 5.6.5 is required
We appended the following line to /etc/my.cnf and restarted MySQL server