Chapter 19: Understanding Transactions (ACID Properties)
Imagine you’re developing an e-commerce application. A user places an order. This single “order placement” action might involve several steps in your database:
- Decrease
stock_quantity
for the ordered product(s) in theproducts
table. - Insert a new record into the
orders
table. - Insert multiple records into the
order_items
table (one for each product in the order). - Update the user’s
last_activity
in theusers
table.
What if step 1 and 2 succeed, but step 3 (inserting order items) fails due to a network error or a database crash? You’d have an order created, but no items associated with it, and stock might be incorrectly reduced. This leaves your database in an inconsistent and incorrect state.
This is where database transactions become absolutely essential. A transaction groups multiple SQL statements into a single, logical unit of work.
What is a Transaction?
A transaction is a sequence of SQL operations that are performed as a single logical unit. Either all of the operations in the transaction complete successfully (COMMIT
), or none of them do (ROLLBACK
). This guarantees the integrity of your data.
The ACID Properties
Database transactions are designed to adhere to a set of properties known as ACID:
- Atomicity: (All or Nothing)
- This is the core principle. A transaction is treated as a single, indivisible unit. If any part of the transaction fails, the entire transaction is aborted (
ROLLBACK
), and the database is returned to its state before the transaction began. If all parts succeed, the transaction is committed (COMMIT
), and changes become permanent. - Analogy: Transferring money between two bank accounts. Either the money is debited from one and credited to the other, or neither happens. You don’t want money to disappear from one account without appearing in the other.
- This is the core principle. A transaction is treated as a single, indivisible unit. If any part of the transaction fails, the entire transaction is aborted (
- Consistency: (Valid State)
- A transaction brings the database from one valid state to another valid state. It ensures that any data written to the database must be valid according to all defined rules, including constraints (e.g.,
PRIMARY KEY
,FOREIGN KEY
,NOT NULL
,CHECK
). - If a transaction attempts to violate a constraint, it is automatically rolled back.
- A transaction brings the database from one valid state to another valid state. It ensures that any data written to the database must be valid according to all defined rules, including constraints (e.g.,
- Isolation: (Independent Execution)
- Concurrent transactions operate independently of each other. The changes made by one transaction are not visible to other transactions until the first transaction is committed. This prevents “dirty reads,” “non-repeatable reads,” and “phantom reads” in multi-user environments.
- Analogy: Multiple people using ATMs simultaneously. Each person sees their account balance and transactions without interference from others’ ongoing transactions.
- Durability: (Permanent Changes)
- Once a transaction has been committed, its changes are permanent and survive subsequent system failures (e.g., power outages, crashes). The committed data is written to persistent storage (disk) and will not be lost.
SQL Syntax & Examples (MySQL/MariaDB)
By default, MySQL runs in autocommit mode, meaning every SQL statement is treated as its own transaction and is automatically committed. To use transactions, you need to disable autocommit for a block of statements or explicitly start a transaction.
1. Starting a Transaction
Syntax:
START TRANSACTION;
-- OR (for older MySQL/MariaDB versions, or if you prefer)
BEGIN;
2. Committing a Transaction
Syntax:
COMMIT;
This makes all changes since START TRANSACTION
permanent.
3. Rolling Back a Transaction
Syntax:
ROLLBACK;
This undoes all changes since START TRANSACTION
, restoring the database to its state before the transaction began.
Example Scenario: Placing an Order
Let’s simulate an order placement process. We’ll decrease stock and create order records within a transaction.
Assume product_id = 1
(‘Laptop Pro’) has stock_quantity = 50
.
USE scriptbuzz_db;
-- 1. Start the transaction
START TRANSACTION;
-- 2. Decrease stock quantity for Laptop Pro
UPDATE products
SET stock_quantity = stock_quantity - 1
WHERE product_id = 1;
-- 3. Insert a new order for user 1 (john_doe)
INSERT INTO orders (user_id, total_amount, status)
VALUES (1, 1200.00, 'processing');
-- Get the last inserted order_id (important for order_items)
-- This is a MySQL-specific function
SET @last_order_id = LAST_INSERT_ID();
-- 4. Insert the order item for Laptop Pro
INSERT INTO order_items (order_id, product_id, quantity, price_per_item)
VALUES (@last_order_id, 1, 1, 1200.00);
-- Now, let's verify the changes (they are not yet permanent!)
-- In a separate session, you might NOT see these changes until committed.
SELECT stock_quantity FROM products WHERE product_id = 1;
SELECT * FROM orders WHERE order_id = @last_order_id;
SELECT * FROM order_items WHERE order_id = @last_order_id;
-- DECISION POINT:
-- If everything went well (no errors), commit the transaction:
-- COMMIT;
-- If something went wrong (e.g., not enough stock, or an insert failed), roll back:
-- ROLLBACK;
Testing the Transaction:
- Run the
START TRANSACTION
and allINSERT
/UPDATE
statements. - Verify (in the same session): The
SELECT
statements after the inserts/updates will show the changes.Laptop Pro
stock will be49
.- A new order and order item will exist.
- Open another MySQL client session (or another tab in Workbench) and run:
USE scriptbuzz_db;
SELECT stock_quantity FROM products WHERE product_id = 1;
SELECT * FROM orders ORDER BY order_id DESC LIMIT 1;
- You should NOT see the updated stock or the new order in this second session, demonstrating Isolation.
- Go back to your first session and run
ROLLBACK;
- Go back to your second session and re-run the
SELECT
statements.Laptop Pro
stock should be back to50
.- The new order and order item will be gone. This demonstrates Atomicity.
- Alternatively, run
COMMIT;
in the first session. - Go back to your second session and re-run the
SELECT
statements.- Now you should see the updated stock and new order, demonstrating Durability.
4. SAVEPOINT
(Advanced Transaction Control)
SAVEPOINT
allows you to set a named point within a transaction to which you can ROLLBACK
without rolling back the entire transaction.
START TRANSACTION;
-- First set of operations
INSERT INTO users (username, email, password) VALUES ('temp_user1', 'temp1@example.com', 'pass');
SAVEPOINT sp1; -- Set a savepoint
-- Second set of operations (maybe problematic)
INSERT INTO users (username, email, password) VALUES ('temp_user2', 'temp2@example.com', 'pass');
-- Oh no, let's say we made a mistake here, or the next step failed
-- Maybe: UPDATE non_existent_table SET col = 1; -- This would cause an error
ROLLBACK TO SAVEPOINT sp1; -- Undoes only changes made AFTER sp1
-- Now continue with other operations
INSERT INTO users (username, email, password) VALUES ('temp_user3', 'temp3@example.com', 'pass');
COMMIT; -- Commits changes up to sp1, plus temp_user3
SAVEPOINT
is useful for complex transactions where you might want to retry parts or handle specific errors without discarding all work.
Common Pitfalls and Best Practices
- Long-Running Transactions: Avoid holding transactions open for too long, especially in a high-concurrency environment. Long transactions can:
- Consume more resources.
- Increase lock contention, slowing down other operations.
- Increase the risk of deadlocks (two transactions blocking each other).
- Missing
COMMIT
orROLLBACK
: Always explicitlyCOMMIT
orROLLBACK
your transactions. If your application crashes or the connection drops before aCOMMIT
, the database might automaticallyROLLBACK
depending on its configuration, but it’s not guaranteed. - Error Handling: In application code, ensure that any database error encountered during a transaction leads to a
ROLLBACK
. ACOMMIT
should only happen if all operations succeed. - Transaction Scope: Design your transactions to be as small and focused as possible while still ensuring atomicity for the logical unit of work.
- Transaction Isolation Levels: Databases offer different isolation levels (e.g.,
READ COMMITTED
,REPEATABLE READ
,SERIALIZABLE
). These levels determine how much one transaction “sees” the uncommitted changes of other transactions.REPEATABLE READ
is the default for InnoDB in MySQL. Understanding these is crucial for advanced concurrency control.
Notes:
- A transaction is a sequence of SQL operations treated as a single, indivisible unit.
- ACID Properties (Atomicity, Consistency, Isolation, Durability) ensure reliable transaction processing.
START TRANSACTION;
(orBEGIN;
) initiates a transaction.COMMIT;
makes all changes permanent.ROLLBACK;
undoes all changes since the transaction started.- Transactions are essential for maintaining data integrity, especially when multiple database operations depend on each other.
- Avoid long-running transactions to minimize resource consumption and lock contention.
Small Exercise
Using your scriptbuzz_db
:
- Start a transaction.
- Update the
price
of ‘Laptop Pro’ (product_id = 1) to1350.00
. - Attempt to insert a new user with a
username
that you know already exists (if you have aUNIQUE
constraint onusername
). Observe the error. - Rollback the transaction.
- Verify that the price of ‘Laptop Pro’ is still its original value and the duplicate user was not inserted.
- Now, start a new transaction.
- Increase the
stock_quantity
of ‘Wireless Mouse’ (product_id = 2) by 50. - Insert a new product called ‘Smart Watch’ with a
price
of299.99
andstock_quantity
of100
. - Commit the transaction.
- Verify that both the stock quantity is updated and the new product exists.
You’ve now tackled transactions, a fundamental concept for building robust and reliable database applications. This knowledge is crucial for handling complex logic and ensuring data integrity! Next, we’ll look at database views.