Snapshots & the four levels
The MySQL 8.4 isolation manual documents four InnoDB levels, with REPEATABLE READ as the server default. These lessons execute Scenarios on MySQL 8.4.11 with InnoDB tables. An application or driver can override the default; inspect its connection settings.
A consistent nonlocking SELECT uses MVCC to read older committed versions instead of waiting for a concurrent row writer. This is not a promise that SELECT never waits: metadata locks can conflict with DDL, and locking SELECTs have different behavior.
The following table gives Documented contracts, not all-level executions. It concerns concurrent changes to InnoDB tables; own writes remain visible.
| Level | Plain table SELECT visibility | Important boundary |
|---|---|---|
| READ UNCOMMITTED | May expose uncommitted versions; no consistent snapshot promise | A dirty value may be rolled back |
| READ COMMITTED | Fresh snapshot per consistent read | Separate SELECTs can disagree after a concurrent commit |
| REPEATABLE READ, default | Snapshot established by the first consistent read | Own writes are visible; UPDATE, DELETE and locking reads use current rows |
| SERIALIZABLE | Plain SELECTs lock in explicit transactions or with autocommit disabled | A standalone autocommit SELECT can use a nonlocking consistent read |
See consistent reads for own-write visibility and DML exceptions, and SERIALIZABLE for the executed autocommit distinction. Do not mix the snapshot rule with a claim about every statement or every SQL function.
The SET TRANSACTION contract distinguishes a setting for the next transaction (SET TRANSACTION) from subsequent session transactions (SET SESSION TRANSACTION). Set the next-transaction level before starting it. MySQL does not use PostgreSQL's BEGIN ISOLATION LEVEL syntax.
READ UNCOMMITTED means it
In this schedule B reads A's uncommitted 999, then reads 100 after A rolls back. The 999 existed as uncommitted data, but never entered committed history. No external application action is executed by this SELECT-result model.
A> BEGIN;
Query OK
A> UPDATE accounts SET balance = 999 WHERE id = 1;
Query OK, 1 row affectedB opts into READ UNCOMMITTED; and sees A's uncommitted 999.
B> SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Query OK
B> BEGIN;
Query OK
B> SELECT @@transaction_isolation AS isolation;
isolation
------------------
READ-UNCOMMITTED
(1 row)
B> SELECT balance FROM accounts WHERE id = 1; -- a dirty read; A never committed this
balance
---------
999
(1 row)A rolls back. The 999 B read never became committed.
A> ROLLBACK;
Query OK
B> SELECT balance FROM accounts WHERE id = 1;
balance
---------
100
(1 row)
B> COMMIT;
Query OKVerified against MySQL 8.4.11 · Run it yourself · Scenario source
READ UNCOMMITTED permits dirty reads; it does not require every read to be dirty. PostgreSQL instead maps that level to READ COMMITTED, as documented in its version 18 isolation manual. Continue with READ COMMITTED, REPEATABLE READ, SERIALIZABLE, and lost updates.