Transactions Dr. Charles Severance www.php-intro.com Transactions Transaction processing is designed to maintain a database Integrity (typically a database or some modern filesystems) in a known, consistent state, by ensuring that interdependent operations on the system are either all completed successfully or all canceled successfully. http://en.wikipedia.org/wiki/Transaction_proces sing Transactions and MySQL CREATE TABLE webauto_rps ( rps_guid VARCHAR(64) NOT NULL, ... ) ENGINE = InnoDB DEFAULT CHARSET=utf8; Bank Balance: 200 ATM ATM Bank Balance: 200 ATM ATM Balance: 200 Bank Balance: 200 ATM ATM Balance: 200 Balance: 200 Bank Balance: 200 ATM ATM Balance: 200 Balance: 200 Bank Balance: 100 ATM ATM Balance: 100 $100 Balance: 200 Bank Balance: 100 ATM ATM Balance: 100 $100 Balance: 100 $100 Bank Balance: 100 ATM ATM Balance: 100 $100 Balance: 100 $100 Awesom e! Transactions • • • Allows a sequence of steps to work as a unit or not work at all Handles the fact that some things take time Locks data on read so that it cannot be read by another transaction unti the current transaction completes Transactions in MySQL • • • • • Tables where transactions will be used require the use of the InnoDB engine. BEGIN - Starts a transaction SELECT .... FOR UPDATE – Reads and locks row (or rows) COMMIT - Completes a transaction ROLLBACK - Gives up on a transaction CREATE TABLE accounts ( number INT, balance FLOAT, PRIMARY KEY(number) ) ENGINE InnoDB; INSERT INTO accounts(number, balance) VALUES(12345, 1025); INSERT INTO accounts(number, balance) VALUES(67890, 140); mysql> select * from accounts; +--------+---------+ | number | balance | +--------+---------+ | 12345 | 1025 | | 67890 | 140 | +--------+---------+ 2 rows in set (0.02 sec) mysql> BEGIN; SELECT balance FROM accounts WHERE number=12345 FOR UPDATE; UPDATE accounts SET balance=balance+25 WHERE number=12345; COMMIT; mysql> select * from accounts; +--------+---------+ | number | balance | +--------+---------+ | 12345 | 1050 | | 67890 | 140 | +--------+---------+ 2 rows in set (0.00 sec) mysql> mysql> BEGIN; mysql> UPDATE accounts SET balance=balance-250 WHERE number=12345; mysql> UPDATE accounts SET balance=balance+250 WHERE number=67890; mysql> SELECT * FROM accounts; +--------+---------+ | number | balance | +--------+---------+ | 12345 | 800 | | 67890 | 390 | +--------+---------+ mysql> ROLLBACK; Query OK, 0 rows affected (0.34 sec) mysql> SELECT * FROM accounts; +--------+---------+ | number | balance | +--------+---------+ | 12345 | 1050 | | 67890 | 140 | +--------+---------+ Transaction started but not committed. SELECT waits Transaction started but not committed. SELECT timeout Transaction started but not committed. UPDATE waits Transaction committed. UPDATE completes instantly Balances properly reflect both reductions Transaction Deadlock • • • If process A Locks X, and process B locks Y, and then process A waits for Y and process B waits for Y - they wait forever This is called a "deadlock" Deadlocks are avoided by making sure all transactions take locks in the same order - this way one will "win" at the beginning not the others will wait at the beginning of the sequence. Start two transactions and lock two accounts Attempt to lock and account but need to wait.... Attempt to lock and already locked account - deadlock First transaction aborted so second select immediately gets lock tsugi/samples/rps/play.php $stmt = $pdo->prepare("SELECT rps_guid, play1, play2, displayname FROM {$p}rps LEFT JOIN {$p}user ON {$p}rps.user1_id = {$p}user.user_id WHERE play2 IS NULL ORDER BY started_at ASC LIMIT 1 FOR UPDATE"); $stmt1 = $pdo->prepare("UPDATE {$p}rps SET user2_id = :U2ID, play2 = :PLAY WHERE rps_guid = :GUID"); $pdo->beginTransaction(); $stmt->execute(); $row = $stmt->fetch(PDO::FETCH_ASSOC); if ( $row == FALSE ) { $pdo->rollBack(); } else { $stmt1->execute(array(":U2ID" => $_SESSION['id'], ":PLAY" => $play, ":GUID" => $row['rps_guid'])); $pdo->commit(); } Transactions • • • Transactions are essential in situations where critical data is read, updated, and written in a way that timing is critical But transactions incur a cost because they lock areas of the database and pause other operations waiting for the transaction to finish There is both a resource and performance cost if transactions are used excessively