Skip to content

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.

LevelPlain table SELECT visibilityImportant boundary
READ UNCOMMITTEDMay expose uncommitted versions; no consistent snapshot promiseA dirty value may be rolled back
READ COMMITTEDFresh snapshot per consistent readSeparate SELECTs can disagree after a concurrent commit
REPEATABLE READ, defaultSnapshot established by the first consistent readOwn writes are visible; UPDATE, DELETE and locking reads use current rows
SERIALIZABLEPlain SELECTs lock in explicit transactions or with autocommit disabledA 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
B
UPDATE balance = 999 (uncommitted)
SELECT balance→ 999 (dirty read)
ROLLBACK; 999 never committed
SELECT balance→ 100 (the real value)
A> BEGIN;
Query OK

A> UPDATE accounts SET balance = 999 WHERE id = 1;
Query OK, 1 row affected

B 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 OK

Verified 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.

Further reading ​

MIT Licensed · Every transcript on this site was generated by a real database run against MySQL 8.4.11 and PostgreSQL 18.6 at 8fa27b8; shared YAML has an independent psycopg/PyMySQL check.