best way to start a mysql transaction in php mysqli and PDO - php

how can i start a transaction in mysqli and PDO? and how can i commit it? i have seen 2 ways : using autocommit(false) and begin_transaction methods , but which one is the best way? i got headache! autocommit(false) or begin_transaction?

Your question isn't clear still I'm answering it. It is straightforward to begin and commit a transaction. In PDO, do something like this:
<?php
$pdo = // connect to database with PDO
$pdo->beginTransaction();
// query database here
$result = // query result
if($result) {
// if result is okay
$pdo->commit();
} else {
$pdo->rollBack();
}
?>
**Explanation: **
First of all, you start a transaction
Next, you query database
If result is okay, commit the transaction
If result is not okay, rollBack and the query will not execute
Reference: http://php.net/manual/en/pdo.transactions.php
About Auto-commit: In auto-commit mode, each query is a complete transaction and it is executed instantly. By default, PDO auto-commits each query. Turning off the auto-commit will need you to commit the query manually. In general scenario, you should not turn it off.
When you need to execute an important query, i.e multi-part queries that depend on each other for their final result like an amount transfer between two accounts (where you need to deduct amount from one table and to add it to another table), simply begin a transaction. PDO will not execute any query until you commit the transaction. If something goes wrong, everything will be rolled back to its previous state.
Lastly, there isn't any big difference between turning off auto-commit and beginning a transaction. By beginning transactions, you can simplify the tasks otherwise you will need to manually commit each query regardless of its nature.
I hope it answers your question.

Related

What is the best way to SELECT from and TRUNCATE an InnoDB table in one atomic operation?

I want to read from an InnoDB table using SELECT and then truncate it using TRUNCATE in one operation such that other queries have to wait for the TRUNCATE operation to complete before they can modify the table. What is the best way to do that? From what I understand, table locks don't work with TRUNCATE. But from my tests, transactions do work with TRUNCATE. However transactions are only guaranteed not to overlap if the isolation level is SERIALIZABLE.
I am using PHP and MySQLi. The default isolation level is REPEATABLE READ so I am changing it to SERIALIZABLE to prevent concurrent queries from modifying the table before the transaction completes:
$mysqli->query("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE");
$mysqli->begin_transaction();
// Select statements reading from `my_table`
$mysqli->query("TRUNCATE `my_table`");
$mysqli->commit();
This seems to be working just fine. I'm just wondering if there is a better way.
Assuming you have error reporting switched on:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
You can use transactions to read and delete the data in one step. I am not sure why you have set different isolation mode, but I assume you don't need it. If I understand it correctly it will lock the rows and prevent deletion.
You can't use TRUNCATE because it is a DDL statement and it does not work with transactions. DELETE is slower, but it can be rolled back.
try {
// Start transaction
$mysqli->begin_transaction();
$result = $mysqli->query('SELECT * FROM my_table');
$mysqli->query('DELETE * FROM my_table');
// Commit changes
$mysqli->commit();
} catch (\Throwable $e) {
// Something went wrong. Rollback
$mysqli->rollback();
throw $e;
}
GET_LOCK() and RELEASE_LOCK() are orthogonal to transactions, etc. (They are extremely rarely used, but your case may need them.)

Try / Catch in a transaction - any difference from using PHP or SQL?

I'm curious about which is the best practice for using Try/Catch in a transaction. Which one would be preferable and why?
Doing the try / catch just in the PHP code:
$dbh->begintransaction();
try {
$insert = "INSERT INTO test (id) VALUES(1)";
$stpm = $dbh->prepare($insert);
$stpm->execute();
$dbh->commit();
} catch (\Exception $e) {
$dbh->rollback();
}
Or directly in the SQL:
$insert = "BEGIN TRANSACTION;
BEGIN TRY
INSERT INTO test (id) VALUES(1)
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH;
IF ##TRANCOUNT > 0
COMMIT TRANSACTION;";
$stpm = $dbh->prepare($insert);
$stpm->execute();
The end result of the two options are the same. Executing a database change with the ability to rollback in the event of error. It is more a question of 'at what level do I want my integrity assurance'.
Code developers will go option 1;
DBAs will go option 2.
IMO though, I'd go option one as it separates the SQL logic execution from the intent. For whatever reason you decide to change DB technology option 1 would allow it with the least amount of effort. (If you want to get real fancy use an ORM like Doctrine, Eloquent, or even Active Record.) Wherein option 2 would requires find/replace and testing of all the possible variations.
TL;DR: Depends on what you are better at; but, abstract when possible.'
Hope this helps.
Use it in PHP code, so that if you have multiple queries and your PHP code fails, these queries will be aborted too.
Using the PDO API or MySQL Transaction is Equal in the performance and affects, Because of PDO is just an abstraction class of SQL statements.
I Advice you to read PDO documentation on transactions, both ways are equivalent because PDO will use the driver-level transaction of a native MySQL transaction.
The Only thing you to decide if you'll perform a mixed collection of sql statements with php manipulations inside them or just a pure group of Mysql statements?

Record deleted during a transaction

I have a system that handles many queries per second. I code my system with mysql and PHP.
My problem is mysqli transaction still commit the transaction even the record is deleted by other user at the same time , all my table are using InnoDB.
This is how I code my transaction with mysqli:
mysqli_autocommit($dbc,FALSE);
$all_query_ok=true;
$q="INSERT INTO Transaction() VALUES()";
mysqli_query ($dbc,$q)?null:$all_query_ok=false;
$q="INSERT INTO Statement() VALUES()";
mysqli_query ($dbc,$q)?null:$all_query_ok=false;
if($all_query_ok==true){
//all success
mysqli_commit($dbc);
}else{
//one of it failed , rollback everything.
mysqli_rollback($dbc);
}
Below are the query performed at the same time in other script by another user and then end up messing the expected system behaviour,
$q="DELETE FROM Transaction...";
mysqli_query ($dbc,$q)?null:$all_query_ok=false;
Please advice , did I implement the transaction wrongly? I have read about row-level locking and believe that innoDB does lock the record during a transaction
I don't know which kind of transactions you're talking about but with the mysqli extension I use the following methods to work with transactions:
mysqli::begin_transaction
mysqli::commit
mysqli::rollback
Then the process is like:
Starting a new transaction with mysqli::begin_transaction
Execute your SQL queries
On success use mysqli::commit to confirm changes done by your queries in step 2 OR on error during execution of your queries in step 2 use mysqli::rollback to revert changes done by them.
You can think of transactions like a temporary cache for your queries. It's someway similar to output caching in PHP with ob_* functions. As long as you didn't have flushed the cached data, nothing happens on screen. Same with transactions: as long as you didn't have commited anything (and autocommit is turned off) nothing happens in the database.
I did some research on row level locking which can lock record from delete or update
FOR UPDATE
Official Documentation
Right after the begin transaction I have to select those record I wanted to lock like below
SELECT * FROM Transaction WHERE id=1 FOR UPDATE
So that the record will be lock until transaction end.
This method doesn't work on MyISAM type table
Looks like a typical example of race condition. You execute two concurrent scripts modifying data in parallel. Probably your first script successfully inserts records and commits the transaction, and the second script successfully deletes records afterwards. I'm not sure what you mean by "the query performed at the same time in other script by other user" though.
You will have to do this this way:
mysqli_autocommit($dbc,FALSE);
$dbc->begin_transaction();
$all_query_ok=true;
$q="INSERT INTO Transaction() VALUES()";
mysqli_query ($dbc,$q)?null:$all_query_ok=false;
$q="INSERT INTO Statement() VALUES()";
mysqli_query ($dbc,$q)?null:$all_query_ok=false;
if($all_query_ok==true){
//all success
mysqli_commit($dbc);
}else{
//one of it failed , rollback everything.
mysqli_rollback($dbc);
}
you can use the object oriented or the procedural style when calling begin_transaction (I prefer the object oriented).

How to rollback the effect of last executed mysql query in php

How to rollback the last executed mysql query.Suppose there are 2-3 queries depend upon submit button and first query run successfully and second failed then how to rollback first query..
Use mysql_rollback or mysqli_rollback depending on whether you are using mysql or mysqli extension
This link should give you a clearer idea
http://www.w3schools.com/php/func_mysqli_rollback.asp
You need to use transactions.
Sequence will be :
1) Begin the transaction
2) Issue one or more SQL commands
3) See if there is any error or not
4) If there is error then issue ROLLBACK if not COMMIT
Note : By default, MySQL runs with autocommit mode enabled. This means that as soon as you execute a statement that updates (modifies) a table, MySQL stores the update on disk to make it permanent.
To disable autocommit mode implicitly for a single series of statements, use the START TRANSACTION statement which will make autocommit disabled until you end the transaction with COMMIT or ROLLBACK.
Refer this
Use transactions:
START TRANSACTION;
SELECT * FROM tbl_foo;
SELECT * FROM llsldllfdlfdld; -- typo, fails!
ROLLBACK; -- reverses first transaction
If everything works, instead of ROLLBACK, send COMMIT; to make the changes final.

MySQL commit and transaction

I have a question regarding MySQL commits and transactions. I have a couple of PHP statements that execute MySQL queries. Do I just say the following?
mysql_query("START TRANSACTION");
//more queries here
mysql_query("COMMIT");
What exactly would this do? How does it help? For updates, deletes and insertions I also found this to block other queries from reading:
mysql_query("LOCK TABLES t1 WRITE, t2 WRITE");
//more queries here
mysql_query("UNLOCK TABLES t1, t2");
Would this block other queries whatever nature or only writes/selects?
Another question: Say one query is running and blocks other queries. Another query tries to access blocked data - and it sees that it is blocked. How does it proceed? Does it wait until the data is unblocked again and re-execute the query? Does it just fail and needs to be repeated? If so, how can I check?
Thanks a lot!
Dennis
In InnoDB, you do not need to explicitly start or end transactions for single queries if you have not changed the default setting of autocommit, which is "on". If autocommit is on, InnoDB automatically encloses every single SQL query in a transaction, which is the equivalent of START TRANSACTION; query; COMMIT;.
If you explicitly use START TRANSACTION in InnoDB with autocommit on, then any queries executed after a START TRANSACTION statement will either all be executed, or all of them will fail. This is useful in banking environments, for example: if I am transferring $500 to your bank account, that operation should only succeed if the sum has been subtracted from my bank balance and added to yours. So in this case, you'd run something like
START TRANSACTION;
UPDATE customers SET balance = balance - 500 WHERE customer = 'Daan';
UPDATE customers SET balance = balance + 500 WHERE customer = 'Dennis';
COMMIT;
This ensures that either both queries will run successfully, or none, but not just one.
This post has some more on when you should use transactions.
In InnoDB, you will very rarely have to lock entire tables; InnoDB, unlike MyISAM, supports row-level locking. This means clients do not have to lock the entire table, forcing other clients to wait. Clients should only lock the rows they actually need, allowing other clients to continue accessing the rows they need.
You can read more about InnoDB transactions here. Your questions about deadlocking are answered in sections 14.2.8.8 and 14.2.8.9 of the docs. If a query fails, your MySQL driver will return an error message indicating the reason; your app should then reissue the queries if required.
Finally, in your example code, you used mysql_query. If you are writing new code, please stop using the old, slow, and deprecated mysql_ library for PHP and use mysqli_ or PDO instead :)

Categories