A transaction is a group of statements treated as one unit, with four guarantees known as ACID:
Atomicity: all statements take effect or none; the undo log reverses them.
Consistency: keys, foreign keys and CHECK constraints (UNIQUE, NOT NULL, CHECK) always hold.
Isolation: transactions do not see each other's unfinished work, thanks to row locks and snapshots (Isolation Levels to Row and Gap Locks).
Durability: a commit survives a crash, because the redo log is flushed at COMMIT while innodb_flush_log_at_trx_commit keeps its default of 1.
The trap is that an error inside a transaction rolls back only the failing statement, not the transaction. Here a double-clicked button adds the same book to order 1 twice:
Session A> START TRANSACTION;
Session A> UPDATE products SET stock = stock - 1 WHERE id = 1;
Session A: Query OK, 1 row affected
Session A> INSERT INTO order_items VALUES (1, 1, 1, 39.90);
Session A: ERROR 1062 (23000) at line 3: Duplicate entry '1-1' for key 'order_items.PRIMARY'
Session A> SELECT stock FROM products WHERE id = 1\G
Session A: *************************** 1. row ***************************
Session A: stock: 24
Session A> ROLLBACK;
Session A> SELECT stock FROM products WHERE id = 1\G
Session A: *************************** 1. row ***************************
Session A: stock: 25The stock decrement survived the error; a COMMIT would have lost a book the shop never sold. Your code must turn any failed statement into a ROLLBACK: PDO in exception mode does it in a catch block (Transactions), and Laravel 2,157 's DB::transaction() on any exception (Transactions and Locks).