SELECT LOCK IN SHARE MODE and FOR UPDATE

Baron wrote nice article comparing locking hints in MySQL and SQL Server.

In MySQL/Innodb LOCK IN SHARE MODE and SELECT FOR UPDATE are more than hints. Behavior will be different from normal SELECT statements. Here is simple example:

 1 SESSION1:
 2 mysql> begin;
 3 Query OK, 0 rows affected (0.00 sec)
 4 mysql> insert into tst values(1);
 5 Query OK, 1 row affected (0.00 sec)
 6 SESSION2:
 7 mysql> begin ;
 8 Query OK, 0 rows affected (0.00 sec)
 9 mysql> select * from tst;
10 Empty set (0.01 sec)
11 #Session2 does not see any rows as transaction was not commited yet.
12 SESSION1:
13 mysql> commit;
14 Query OK, 0 rows affected (0.01 sec)
15 SESSION2:
16 mysql> select * from tst;
17 Empty set (0.00 sec)
18 mysql> select * from tst lock in share mode;
19 +---+
20 | i |
21 +---+
22 | 1 |
23 +---+
24 1 row in set (0.00 sec)
25 mysql> select * from tst for update;
26 +---+
27 | i |
28 +---+
29 | 1 |
30 +---+
31 1 row in set (0.00 sec)
32 #Standard SELECT does not see rows while SELECT for UPDATE and LOCK IN SHARE MODE  sees it. 

 

What is happening ? SELECT for UPDATE and LOCK IN SHARE MODE modifiers effectively run in READ-COMMITTED isolation mode even if current isolation mode is REPEATABLE-READ. This is done beause Innodb can only lock current version of row. Think about similar case and row being deleted. Even if Innodb would be able to set locks on rows which no more exist – would it do any good for you ? Not really – for example you could try to update the row which you just locked with SELECT FOR UPDATE but this row is already gone so you would get quite unexpected error updating the row which you thought you locked successfully. Anyway it is done this way for good all other decisions would be even more troublesome. This complexity is what you have to pay for multiversioning.

Lets also think how these modifiers can be user and what do we expect from them in practice. LOCK IN SHARE MODE is actually often used to bypass multiversioning and make sure we’re reading most current data, plus to ensure it can’t be changed. This for example can be used to read set of the rows, compute new values for some of them and write them back. If we would not use LOCK IN SHARE MODE we could be in trouble as rows could be update before we write new values to them and such update could be lost. Note I said some of them. If you want to read set of rows and modify all of them you may chose to use SELECT FOR UPDATE. This will ensure you get write locks for all rows at once which reduces chance of deadlocks – lock will not need to be upgraded when update happens. SELECT FOR UPDATE also blocks access to the data using LOCK IN SHARE MODE. So by using these two modifiers you may effectively implement instant data invalidation – using SELECT FOR UPDATE to quickly lock data which is no more correct so it is not used while you recompute it. Note it also works if LOCK IN SHARE MODE is used with selects – standard selects are run in non-locking mode which means they never lock any rows and just use old row versions if they were updated.

All said above applies to default REPEATABLE-READ mode. With different isolation modes there could be some differences but logic stills the same.

So these hints are very powerful and helpful for application development but should be used wisely. Do not assume you can simply add SELECT FOR UPDATE to your select and reduce deadlocks if you’re updating selected rows. As query results may chance you need to access how it affects your application and perform changes required.

What is missing in Innodb locking.? In my oppinion few rather important peices missing in Locking implementation of Innodb are:

Lock table Innodb can lock tables but it will still need to set row level locks which is memory and CPU overhead. For some bulk operations it would be more efficient to use table locks. As I tested it really takes some resources.

Unlocking non matched rows Imagine you’re running DELETE FROM USERS WHERE NAME LIKE “%Heikki%”; How any rows do you think will be locked ? Actually all of them, not only ones which are matched by like because locks are taken on Innodb level before MySQL performs like matching, and row is not unlocked if it does not match.

Smarter deadlock victum scheduling At this point transaction which made least updates is killed to resolve deadlock. Which means if transaction takes a lot of locks but does not do much updates it may never have chance to complete. The best example would be
INSERT INTO MyISAMTable SELECT * FROM INNODBTable; – A lot of shared locks on Innodb table but no updates. Supporting MySQL hints
“HIGH_PRIORITY” and “LOW_PRIORITY” would probably be good start.

参考:

http://www.mysqlperformanceblog.com/2006/08/06/select-lock-in-share-mode-and-for-update/

posted on 2014-02-19 01:25  Still water run deep  阅读(393)  评论(0编辑  收藏  举报

导航