✏️ Explanatory Question
Level: Very Hard — A real reporting bug where numbers "don't add up" because data changes mid-report.
orders (at report start)| order_id | amount |
|---|---|
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
| Time | Report Transaction | Another User | Rows seen |
|---|---|---|---|
| t1 | Query 1: SUM(amount) = 600 | 3 rows | |
| t2 | INSERT order (id 4, 400); COMMIT | ||
| t3 | Query 2: COUNT(*) = 4 | 4 rows! |
SUM says 600 (3 orders) but COUNT says 4 — a "phantom" row appeared mid-transaction. The report is internally inconsistent.
The root cause: Under READ COMMITTED, each query sees the latest committed data — so the second query sees the newly inserted "phantom" row that the first didn't. The two queries ran against different states of the table.
REPEATABLE READ (InnoDB's default). It takes a snapshot at the first read, and every subsequent query in that transaction sees the same frozen state — new inserts are invisible until the transaction ends. Both queries then agree.
-- Ensure the whole report sees ONE consistent point-in-time snapshot
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION; -- snapshot established at first read (MVCC)
-- Query 1: sum
SELECT SUM(amount) AS total_revenue FROM orders; -- 600
-- ... even if others INSERT/COMMIT new orders here ...
-- Query 2: count -> still sees the SAME 3 rows as Query 1
SELECT COUNT(*) AS total_orders FROM orders; -- 3 (consistent!)
COMMIT; -- snapshot released
-- Forces the snapshot to be taken immediately at BEGIN,
-- not lazily at the first SELECT
START TRANSACTION WITH CONSISTENT SNAPSHOT;
SELECT SUM(amount) FROM orders;
SELECT COUNT(*) FROM orders; -- guaranteed same snapshot
COMMIT;
| Level | Report consistency | Concurrency impact |
|---|---|---|
| READ COMMITTED | Phantoms possible | High (good) |
| REPEATABLE READ | Consistent snapshot | High (MVCC, no read locks) |
| SERIALIZABLE | Fully consistent | Low (locks reads) |