Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Locking mySQL tables/rows

Tags:

mysql

can someone explain the need to lock tables and/or rows in mysql?

I am assuming that it to prevent multiple writes to the same field, is this the best practise?

like image 268
Mild Fuzz Avatar asked Jan 11 '11 15:01

Mild Fuzz


3 Answers

First lets look a good document This is not a mysql related documentation, it's about postgreSQl, but it's one of the simplier and clear doc I've read on transaction. You'll understand MySQl transaction better after reading this link http://www.postgresql.org/docs/8.4/static/mvcc.html

When you're running a transaction 4 rules are applied (ACID):

  • Atomicity : all or nothing (rollback)
  • Coherence : coherent before, coherent after
  • Isolation: not impacted by others?
  • Durability : commit, if it's done, it's really done

In theses rules there's only one which is problematic, it's Isolation. using a transaction does not ensure a perfect isolation level. The previous link will explain you better what are the phantom-reads and suchs isolation problems between concurrent transactions. But to make it simple you should really use Row levels locks to prevent other transaction, running in the same time as you (and maybe comitting before you), to alter the same records. But with locks comes deadlocks...

Then when you'll try using nice transactions with locks you'll need to handle deadlocks and you'll need to handle the fact that transaction can fail and should be re-launched (simple for or while loops).

Edit:------------

Recent versions of InnoDb provides greater levels of isolation than previous ones. I've done some tests and I must admit that even the phantoms reads that should happen are now difficult to reproduce.

MySQL is on level 3 by default of the 4 levels of isolation explained in the PosgtreSQL document (where postgreSQL is in level 2 by default). This is REPEATABLE READS. That means you won't have Dirty reads and you won't have Non-repeatable reads. So someone modifying a row on which you made your select in your transaction will get an implicit LOCK (like if you had perform a select for update).

Warning: If you work with an older version of MySQL like 5.0 you're maybe in level 2, you'll need to perform the row lock using the 'FOR UPDATE' words!

We can always find some nice race conditions, working with aggregate queries it could be safer to be in the 4th level of isolation (by using LOCK IN SHARE MODE at the end of your query) if you do not want people adding rows while you're performing some tasks. I've been able to reproduce one serializable level problem but I won't explain here the complex example, really tricky race conditions. There is a very nice example of race conditions that even serializable level cannot fix here : http://www.postgresql.org/docs/8.4/static/transaction-iso.html#MVCC-SERIALIZABILITY

When working with transactions the more important things are:

  • data used in your transaction must always be read INSIDE the transaction (re-read it if you had data from before the BEGIN)
  • understand why the high isolation level set implicit locks and may block some other queries ( and make them timeout)
  • try to avoid dead locks (try to lock tables in the same order) but handle them (retry a transaction aborted by MySQL)
  • try to freeze important source tables with serialization isolation level (LOCK IN SHARE MODE) when your application code assume that no insert or update should modify the dataset he's using (if not you will not have problems but your result will have ignored the concurrent changes)
like image 199
regilero Avatar answered Oct 18 '22 23:10

regilero


It is not a best practice. Modern versions of MySQL support transactions with well defined semantics. Use transactions, and forget about locking stuff by hand.

The only new thing you'll have to deal with is that transaction commits may fail because of race conditions, but you'd be doing error checking with locks anyway, and it is easier to retry the logic that led to a transaction failure than to recover from errors in a non-transactional setup.

If you do get race conditions and failed commits, then you may want to fine-tune the isolation configuration for your transactions.

like image 2
Apalala Avatar answered Oct 18 '22 21:10

Apalala


For example if you need to generate invoice numbers which are sequential and have no numbers missing - this is a requirement at least in the country I live in.

If you have a few web servers, then a few users might be buying stuff literally at the same time.

  1. If you do select max(invoice_id)+1 from invoice to get the new invoice number, two web servers might do that at the same time (before the new invoice has been added), and get the same invoice number for the invoices they're trying to create.

  2. If you use a mechanism such as "auto_increment", this is just meant to generate unique values, and makes no guarantees about not missing out numbers (if one transaction tries to insert a row, then does a rollback, the number is "lost"),

So the solution is to (a) lock the table (b) select max(invoice_id)+1 from invoice (c) do the insert (d) commit + unlock the table.

On another note, in MySQL you're best using InnoDB and using row-level locking. Doing a lock table command can implicitly commit the transaciton you're working on.

Take a look here for general introduction to what transactions are and how to use them.

like image 1
Adrian Smith Avatar answered Oct 18 '22 22:10

Adrian Smith