Skip to main content

How to Use a Read-Only Transaction in MySQL

· 4 min read
Jagdish Kumawat
Founder @ Dewiride

A report runs several queries, and if the numbers move underneath it, they won't add up. A read-only transaction with a consistent snapshot pins down what it sees and guarantees it can't change anything by accident. Here's what each part does.

Prerequisites​

Step 1: Refuse Changes with READ ONLY​

mysql
START TRANSACTION READ ONLY;
SELECT Id, Sku, QuantityOnHand FROM products WHERE Id = 3;
Output (the SELECT)
+----+-------------+----------------+
| Id | Sku | QuantityOnHand |
+----+-------------+----------------+
| 3 | CBL-HDMI-2M | 50 |
+----+-------------+----------------+
1 row in set (0.002 sec)

Reading works. Changing doesn't:

mysql
UPDATE products SET QuantityOnHand = 49 WHERE Id = 3;
Output
ERROR 1792 (25006): Cannot execute statement in a READ ONLY transaction.

A mysql session in a READ ONLY transaction, where a SELECT works and an UPDATE fails with ERROR 1792 Cannot execute statement in a READ ONLY transaction.

MySQL's START TRANSACTION page says: "The READ ONLY restriction prevents the transaction from modifying or locking both transactional and nontransactional tables that are visible to other transactions; the transaction can still modify or lock temporary tables."

Step 2: Pin What It Sees​

Add WITH CONSISTENT SNAPSHOT, and the transaction's view is fixed the moment it starts:

mysql: terminal 1
COMMIT;
START TRANSACTION READ ONLY, WITH CONSISTENT SNAPSHOT;

Someone else changes the row:

mysql: terminal 2
UPDATE products SET QuantityOnHand = 48 WHERE Id = 3;

The report doesn't see it, however many queries it runs:

mysql: terminal 1
SELECT Id, Sku, QuantityOnHand FROM products WHERE Id = 3;
Output
+----+-------------+----------------+
| Id | Sku | QuantityOnHand |
+----+-------------+----------------+
| 3 | CBL-HDMI-2M | 50 |
+----+-------------+----------------+
1 row in set (0.002 sec)

When the report is done, the new value is there:

mysql: terminal 1
COMMIT;
SELECT Id, Sku, QuantityOnHand FROM products WHERE Id = 3;
Output (the SELECT)
+----+-------------+----------------+
| Id | Sku | QuantityOnHand |
+----+-------------+----------------+
| 3 | CBL-HDMI-2M | 48 |
+----+-------------+----------------+
1 row in set (0.001 sec)

READ COMMITTED vs REPEATABLE READ in MySQL shows when the snapshot starts without this option.

What It Can Still Change​

Temporary tables belong to your session, so they're allowed, which is handy for a report's working data:

mysql
CREATE TEMPORARY TABLE scratch (Id int PRIMARY KEY);
START TRANSACTION READ ONLY;
INSERT INTO scratch VALUES (1);
COMMIT;
Output (the INSERT)
Query OK, 1 row affected (0.001 sec)

Create the temporary table before the transaction: CREATE TEMPORARY TABLE isn't allowed in one.

Common Mistakes​

MistakeWhat happensDo this instead
Using it for code that also writesERROR 1792Use a normal transaction
Expecting READ ONLY alone to fix the snapshotThe snapshot starts at the first readAdd WITH CONSISTENT SNAPSHOT
Keeping it open for hoursInnoDB has to keep the old row versionsFinish the report, and commit

Troubleshooting​

SymptomLikely causeFix
Cannot execute statement in a READ ONLY transactionA write inside itThe fix
The report still sees new dataREAD COMMITTED is in useA snapshot needs REPEATABLE READ
Transaction characteristics can't be changedSET TRANSACTION READ ONLY inside a transactionRun it first

FAQ​

Does it hold locks?​

Not with plain SELECTs. So a long report doesn't block writers, and they don't block it.

Conclusion​

START TRANSACTION READ ONLY, WITH CONSISTENT SNAPSHOT gives a report a fixed view of the data and refuses any write, without blocking anyone. End it with COMMIT, and the next read sees the newest data.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.