MySQL: LOCK TABLES with autocommit vs. START TRANSACTION
Asked Answered
N

2

7

In the MySQL docs there is a statement I don't understand:

The correct way to use LOCK TABLES and UNLOCK TABLES with transactional tables, such as InnoDB tables, is to begin a transaction with SET autocommit = 0 (not START TRANSACTION) followed by LOCK TABLES, and to not call UNLOCK TABLES until you commit the transaction explicitly. (https://dev.mysql.com/doc/refman/5.7/en/lock-tables-and-transactions.html)

Even after searching a lot in the docs and studying the description for "autocommit" and "START TRANSACTION", I really don't understand why to use autocommit instead of START TRANSACTION. Any ideas? Thanks

Nazarite answered 26/5, 2018 at 6:2 Comment(0)
M
12

LOCK TABLES implicitly commits a transaction, according to https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html

So if you were to do the following sequence, it would not do what you intend:

BEGIN;
/* I think I'm in transaction 1 */
LOCK TABLES ...;
/* but that implicitly committed transaction 1 */

UPDATE blab blah ...;
/* I think I'm still in transaction 1, but this update caused 
   a new transaction 2 to begin and autocommit */ 
UPDATE yadda yadda ...;
/* I think I'm still in transaction 1, but this update caused 
   a new transaction 3 to begin and autocommit */ 
COMMIT;

The point of using transactions is when you need to run multiple updates, but you want the result of all updates to commit together or not at all.

The above example does not commit the two updates together atomically. They each have their own autocommitted transaction. Therefore one might succeed but the other doesn't.

The recommendation is to do the following:

SET autocommit=0;
LOCK TABLES ...;
/* that implicitly committed any outstanding transaction, but that's OK */

UPDATE blab blah ...;
/* because autocommit=0, a new DML statement implicitly starts a new transaction */ 
UPDATE yadda yadda ...;
/* the new transaction is still open */
COMMIT;

UNLOCK TABLES;

This commits both updates together, atomically.

Why not just LOCK TABLES and then BEGIN to start a transaction?

https://dev.mysql.com/doc/refman/5.7/en/lock-tables-and-transactions.html says:

Beginning a transaction (for example, with START TRANSACTION) implicitly commits any current transaction and releases existing table locks.

That seems strange to me. An explicit BEGIN (or START TRANSACTION) releases table locks, but an implicit start of a transaction does not? That feature is full of WTF, in my opinion. But that's what's documented.

Marseilles answered 26/5, 2018 at 20:8 Comment(8)
Thanks Bill, that makes it much clearer. (Sorry that I can't upvote your answer because of my reputation.)Nazarite
@BillKarwin I went for my copy of one of my all-time favorite reference books, about half expecting to find a chapter about mixing table locks and transactions. It's not there, but I'm curious... doesn't it seem to you like mixing the two should generally be unnecessary? I can't think of a situation off-hand where transactions, and their accompanying locks, shouldn't be sufficient (not to mention less disruptive). Thoughts?Aurist
@Michael-sqlbot, I hardly ever use table locks — they're liable to hinder concurrent access to the table. But their use is to get around the optimistic locking of InnoDB. You can lock several tables at once atomically with one LOCK TABLES statement. That would be an effective way to avoid deadlocks, but it serializes the access to the tables. And thanks very much for reading my book!Marseilles
Thanks Bill. Great answer.Phonolite
absolutely true! this feature is full of WTFsLactose
Beginning a transaction (for example, with START TRANSACTION) implicitly commits any current transaction and releases existing table locks - @Bill Karwin, does this mean that we can only have ONE ACTIVE transaction per session? Thanks in advanceBridgman
@Daniel, Yes, that's correct for MySQL and most other SQL databases. I've used InterBase and its open source fork Firebird that allow multiple open transactions per session, but as far as I know that's the only one.Marseilles
Excellent @Bill Karwin, thanks + 2Bridgman
S
-1
  • Never (well, I have not heard of a case) use LOCK TABLES with InnoDB. LOCK TABLES is a sledgehammer. It may severely slow down your system.

  • For certain apps, autocommit=ON is fine.

  • For certain apps, use BEGIN (START TRANSACTION) and COMMIT -- Keep them cleanly paired up.

  • Do not use autocommit=OFF, it is too easy to forget to do COMMIT.

  • Remember that DDL statements implicitly COMMIT. (Until MySQL 8.0.)

Scamander answered 28/5, 2018 at 19:31 Comment(2)
Even on database session_set_save_handler() -> write() ? (LOCK TABLES session WRITE / UNLOCK TABLES)Glaucous
@Glaucous - I don't use PHP's "sessions" at all -- it does not allow for scaling the clients across multiple servers. Also, I would not want to risk timing out because of holding onto something too long in the "session" mechanism.Scamander

© 2022 - 2024 — McMap. All rights reserved.