It is said on the PHP docs site that:
while the transaction is active, you are guaranteed that no one else
can make changes while you are in the middle of your work
ref: http://php.net/manual/en/pdo.transactions.php
The question is: if I start transaction in Script A and then Script B tries to makes any changes before Script A commits -- what would be the outcome for the Script B? Will it "wait"? Will it fail with error? What exactly?
Transactions are controlled at the DBMS level, not by PHP. So if something is blocked, it's done by your DBMS. This varies, because different database engines block at different levels. For example, MySQL MyISAM engine will lock at the table level whereas the MySQL InnoDB engine can block at row-level. Locking can also happen at a per-session or global basis depending on how you configure your DBMS and how the transaction is performed. PDO only implements the driver for your vendor-specific DBMS so it has no real say in all of that.
Related
I have an app which takes heartbeats (a simple http request) from hosts, typically the host generates one request every x minutes. This results in a large number of completely independent php pages runs which does a read queries then (possibly) generates one single row insert to the RDS database, which doesn't really matter if it succeeds (one missed beat isn't a reason for alarm, several are)
However, with mysqli I have a significant overhead in IOPs - it sends a BEGIN, my single line insert, then a COMMIT - and therefore appears to use three IOPs where I only need one.
Is there any way to avoid the transactions entirely? I could change auto_commit, but it's useless as each run of the handler is separate, so there is no other insert to group with this one. Even turning auto_commit off still runs a transaction, but only ends it when the connection closes (which happens after one insert anyway.)
Or should I switch to raw mysql handling for efficiency (lots of work)? The old mysql php library (deprecated)? Something else?
If you really don't need transactions you can use MyISAM storage engine for your tables that doesn't support transactions.
I use always :
SET AUTOCOMMIT=1
for my InnoDB databases where I don't need use transactions just after connecting to database.
I need to put multiple values into 2 databases. The thing is, that if one of those INSERTS fails, i need all the other to rollback.
The question
Is it possible, to make simultaneously two transactions, inserting some values into databases, and then Commit or rollback both of them?
The Code
$res = new ResultSet(); //class connecting and letting me query first database
$res2 = new ResultSet2(); //the other database
$res->query("BEGIN");
$res2->query("BEGIN");
try
{
$res->query("INSERT xxx~~") or wyjatek('rollback'); //wyjatek is throwing exception if query fails
$res2->query("INSERT yyy~~")or wyjatek('rollback');
......
//if everything goes well
$res->query("COMMIT");
$res2->query("COMMIT");
//SHOW some GREEN text saying its done.
}
catch(Exception $e)
{
//if wyjatek throws an exception
$res->query("ROLLBACK");
$res2->query("ROLLBACK");
//SHOW some RED text, saying it failed
}
Summary
So is it proper way, or will it even work?
All tips appreciated.
Theoretically
If you will remove
or wyjatek('rollback')
your script will be work.
But looking to documentation
Transactions are isolated within a single "database". If you want to use multiple database transactions using MySql you can see XA Transactions.
Support for XA transactions is available for the InnoDB storage
engine.
XA supports distributed transactions, that is, the ability to permit
multiple separate transactional resources to participate in a global
transaction. Transactional resources often are RDBMSs but may be other
kinds of resources.
An application performs actions that involve different database
servers, such as a MySQL server and an Oracle server (or multiple
MySQL servers), where actions that involve multiple servers must
happen as part of a global transaction, rather than as separate
transactions local to each server.
The XA Specification. This
document is published by The Open Group and available at
http://www.opengroup.org/public/pubs/catalog/c193.htm
What about letting PostgreSQL doing the dirty work?
http://www.postgresql.org/docs/9.1/static/warm-standby.html#SYNCHRONOUS-REPLICATION
What you propose will almost always work. But for some uses, 'almost always' is not good enough.
If you have deferred constraints, the commit on $res2 could fail on a constraint violation, and then it is too late to rollback $res.
Or, one of your servers or the network could fail between the first commit and the second. If the php, database1, and database2 are all on the same hardware, the window for this failure mode is pretty small, but not negligible.
If 'almost always' is not good enough, and you cannot migrate one set of data to live inside the other database, then you might need to resort to "prepared transactions".
I have an innodb table that I want to perform on some maintenance queries, those queries are going to happen on parallel threads, and they will include (in that order):
Select, update, select, delete, insert.
I want to only allow 1 single parallel thread to have access to that section, so is there something that would allow me to do this?:
mutex.block()
select
update
select
delete
insert
mutex.release()
This will be in php, and all queries will be executed using php's function mysqli_query.
I am hoping for an alternate to transactions, if nothing but transaction can be done here, then be it.
MySQL features table locks and locks (a kind of mutex).
PHP supports pretty much all POSIX locking mechanisms, such as mutexes and semaphores (but these are not available by default, see the related "Installing/Configuring" manual chapters).
But really, I see little reason why you would want to implement your own synchronisation mechanism: transactions exist for this purpose precisely, and are likely to be more efficient and reliable than anything home-brewed.
(if performance is your concern, then a database back-end may not be the right choice)
Can I use this function instead of 'LOCK TABLES' query?
Example:
pdo::beginTransaction();
SELECT id AS last_id FROM t WHERE...
INSERT INTO t (id,...) VALUES (last_id+1,....)
pdo::commit();
The answer to this is: it depends. The most important resource to help you understand exactly what it does, what it doesn't do and how it works is here.
For a kick off, it depends whether the underlying driver (MySQL, MSSQL, etc etc) supports transaction functionality at all. If the driver does not support transactions, pdo::beginTransaction(); will fail and return FALSE, and all your queries will be executed immediately. This is not to say that a LOCK TABLES query would fail - it depends whether the underlying database engine supports it.
In fact, in MySQL at least, pdo::beginTransaction() follows the same rules as a START TRANSACTION statement. It does not lock the table immediately, it simply ensures that that the queries that are part of the transaction follow the rules of ACID.
I won't go into full details of exactly which combinations will work and what they will do because they are covered at length in the documents I linked to, and there is no sense in me writing it all out again.
For fun I am replacing the mysqli extension in my app with PDO.
Once in awhile I need to use transactions + table locking.
In these situations, according to the mysql manual, the syntax needs to be a bit different. Instead of calling START TRANSACTION, you do it like so...
SET autocommit=0;
LOCK TABLES t1 WRITE, t2 READ, ...;
... do something with tables t1 and t2 here ...
COMMIT;
UNLOCK TABLES;
(http://dev.mysql.com/doc/refman/5.0/en/lock-tables-and-transactions.html)
My question is, how does this interact with PDO::beginTransaction? Can I use PDO::beginTransaction in this case? Or should I manually send the sql "SET autocommit = 0; ... etc".
Thanks for the advice,
When you call PDO::beginTransaction(), it turns off auto commit.
So you can do:
$db->beginTransaction();
$db->exec('LOCK TABLES t1, t2, ...');
# do something with tables
$db->commit();
$db->exec('UNLOCK TABLES');
After a commit() or rollBack(), the database will be back in auto commit mode.
I have spent a huge amount of time running around this issue, and the PHP documentation in this area is vague at best. A few things I have found, running PHP 7 with a MySQL InnoDB table:
PDO::beginTransaction doesn't just turn off autocommit, having tested the answer provided by Olhovsky with code that fails, rollbacks do not work; there is no transactional behaviour. This means it can't be this simple.
Beginning a transaction may be locking the used tables... I eagerly await for someone to tell me I'm wrong with this, but here are the reasons it could be: This comment, which shows a table being inaccessible when a transaction has started, without being locked. This PHP documentation page, that slips in on the end:
... while the transaction is active, you are guaranteed that no one else can make changes while you are in the middle of your work
To me this behaviour is quite smart, and also provides enough wiggle room for PDO to cope with every database, which is after all the aim. If this is what is going on though, its just massively under documented and should've been called something else to avoid confusion with a true database transaction, which doesn't imply locking.
Charles' answer I think is probably the best if you are after certainty with a workload that will require high concurrency; do it by hand using explicit queries to the database, then you can go by the database's documentation.
Update
I have had a production server up and running using the PDO transaction functions for a while now, recently using AWS's Aurora database (fully compatible with MySQL but built to automatically scale etc). I have proven these two points to myself:
Transactions (purely the ability to commit all database changes together) work using PDO::beginTransaction(). In short, I know many scripts have failed half way through their database select/updates and data integrity has been maintained.
Table locking isn't happening, I've had an index duplication error to prove this.
So, to further my conclusion, looks like the behaviour of these functions seems to change based on database engine (and possibly other factors). As far as I can tell both from experience and the documentation, there is no way to know programmatically what is going on... whoop...
In MySQL, beginning a transaction is different than turning off autocommit, due to how LOCK/UNLOCK TABLES works. In MySQL, LOCK TABLES commits any open transactions, but turning off autocommit isn't actually starting a transaction. MySQL is funny that way.
In PDO, starting a transaction using beginTransaction doesn't actually start a new transaction, it just turns off autocommit. In most databases, this is sane, but it can have side effects with MySQL's mentioned behavior.
You probably should not rely on this behavior and how it interacts with MySQL's quirks. If you're going to deal with MySQL's behavior for table locking and DDL, you should avoid it. If you want autocommit off, turn it off by hand. If you want to open a transaction, open a transaction by hand.
You can freely mix the PDO API methods of working with transactions and SQL commands when not otherwise working with MySQL's oddities.