From: Baron Schwartz Date: January 15 2010 2:14am Subject: Re: When using "FOR UPDATE" whole the table seems to lock instead of selected row List-Archive: http://lists.mysql.com/mysql/220253 Message-Id: <4cfa0b031001141814s6a5d045fsa3ef2ee1f886fd00@mail.gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Johan, I don't see a valid need for using FOR UPDATE here. In fact, FOR UPDATE is the cause of many grievances, and I would advise you to avoid it by any means possible. Among other things, it will cause serious performance problems when your server gets busy. And as you can see, it's hard to figure out why it behaves as it does. Simply issue the UPDATE statement without a SELECT first. - Baron On Thu, Jan 14, 2010 at 4:08 AM, Johan Machielse wrote: > Hi, > > I have created a query to read and update a stock item by using the FOR U= PDATE statement. According to the MySql documention only the rows that are = selected using the FOR UPDATE should be locked for other sessions, but some= how whole the table is locked. This post gives some general information and= then shows 3 scenarios. The first 2 scenarios work as expected, but the la= st one fails. > > Two questions: > =A0a.. Does anyone has an idea why scenario 3 does not work as expected a= nd what is the solution to make it work? > =A0b.. Is this the preferred way to update a stock table? If not, could y= ou provide me an example how it should be done? > Here some clarification about the problem: > > Version MySQL > 5.0.45-community-nt > > Stock table > DROP TABLE IF EXISTS `mydatabase`.`stock`; > CREATE TABLE =A0`mydatabase`.`stock` ( > =A0`ID` bigint(20) unsigned NOT NULL auto_increment, > =A0`ProductID` bigint(20) unsigned NOT NULL, > =A0`SizeID` bigint(20) unsigned NOT NULL, > =A0`Quantity` int(11) NOT NULL, > =A0PRIMARY KEY =A0USING BTREE (`ID`) > ) ENGINE=3DInnoDB AUTO_INCREMENT=3D3 DEFAULT CHARSET=3Dlatin1; > > MySQL example > This query updates the stock of one product of a particular size by decre= menting it's quantity by 1. > > START TRANSACTION; > SET AUTOCOMMIT=3D0; > SELECT Stock.Quantity FROM Stock WHERE Stock.ProductID =3D 1 AND Stock.Si= zeID =3D 2 FOR UPDATE; > UPDATE Stock SET Stock.Quantity =3D Stock.Quantity - 1 WHERE Stock.Produc= tID =3D 1 AND Stock.SizeID =3D 2; > COMMIT; > > Now let me show you three scenarios wherein you can see that using the FO= R UPDATE statement is not working as it should. > > Scenario 1 (works as expected) > I opened two sessions of MySql Query Browser and each executing the same = MySql query. So, both looking at the same row (same selection criteria). > > START TRANSACTION; > SET AUTOCOMMIT=3D0; > SELECT Stock.Quantity FROM Stock WHERE Stock.ProductID =3D 1 AND Stock.Si= zeID =3D 2 FOR UPDATE; > UPDATE Stock SET Stock.Quantity =3D Stock.Quantity - 1 WHERE Stock.Produc= tID =3D 1 AND Stock.SizeID =3D 2; > COMMIT; > > 1) Session 1: START TRANSACTION; > 2) Session 1: SET AUTOCOMMIT=3D0; > 3) Session 1: SELECT Stock.Quantity FROM Stock WHERE Stock.ProductID =3D = 1 AND Stock.SizeID =3D 2 FOR UPDATE; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(returns Stock.Quantity as expecte= d) > 4) Session 2: START TRANSACTION; > 5) Session 2: SET AUTOCOMMIT=3D0; > 6) Session 2: SELECT Stock.Quantity FROM Stock WHERE Stock.ProductID =3D = 1 AND Stock.SizeID =3D 2 FOR UPDATE; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(blocks as exepected) > 7) Session 1: UPDATE Stock SET Stock.Quantity =3D Stock.Quantity - 1 WHER= E Stock.ProductID =3D 1 AND Stock.SizeID =3D 2; > 8) Session 1: COMMIT; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(the blocking step 6 is now execut= ed and returns the updated Stock.Quantity as exepected) > 9) Session 2: UPDATE Stock SET Stock.Quantity =3D Stock.Quantity - 1 WHER= E Stock.ProductID =3D 1 AND Stock.SizeID =3D 2; > 10) Session 2: COMMIT; > > Scenario 2 (works as expected) > I opened two sessions of MySql Query Browser and each executing the same = MySql query. Only the selection criteria are different; > both are using another value for the primary key "ID". When I use differe= nt =A0values for the primary key "ID" I have the same results > as in scenario 1. > > Session 1 query > START TRANSACTION; > SET AUTOCOMMIT=3D0; > SELECT Stock.Quantity FROM Stock WHERE Stock.ID =3D 1 FOR UPDATE; > UPDATE Stock SET Stock.Quantity =3D Stock.Quantity - 1 WHERE Stock.ID =3D= 1; > COMMIT; > > Session 2 query > START TRANSACTION; > SET AUTOCOMMIT=3D0; > SELECT Stock.Quantity FROM Stock WHERE Stock.ID =3D 2 FOR UPDATE; > UPDATE Stock SET Stock.Quantity =3D Stock.Quantity - 1 WHERE Stock.ID =3D= 2; > COMMIT; > > 1) Session 1: START TRANSACTION; > 2) Session 1: SET AUTOCOMMIT=3D0; > 3) Session 1: SELECT Stock.Quantity FROM Stock WHERE Stock.ID =3D 1 FOR U= PDATE; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(returns Stock.Quantity as expecte= d) > 4) Session 2: START TRANSACTION; > 5) Session 2: SET AUTOCOMMIT=3D0; > 6) Session 2: SELECT Stock.Quantity FROM Stock WHERE Stock.ID =3D 2 FOR U= PDATE; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(no blocking as exepected, because= it's another row; returns Stock.Quantity as expected) > 7) Session 1: UPDATE Stock SET Stock.Quantity =3D Stock.Quantity - 1 WHER= E Stock.ID =3D 1; > 8) Session 1: COMMIT; > 9) Session 2: UPDATE Stock SET Stock.Quantity =3D Stock.Quantity - 1 WHER= E Stock.ID =3D 2; > 10) Session 2: COMMIT; > > Scenario 3 (does not work as expected) > I opened two sessions of MySql Query Browser and each executing the same = MySql query. Only the selection criteria are different; > both are using another value for the non-primary key "SizeID". Making Siz= eID a primary key does not influence the results of this > scenario. So, the sessions work in different rows, but session 2 is block= ed! It looks like the whole table is locked instead of only one row? > > Session 1 query > START TRANSACTION; > SET AUTOCOMMIT=3D0; > SELECT Stock.Quantity FROM Stock WHERE Stock.ProductID =3D 1 AND Stock.Si= zeID =3D 1 FOR UPDATE; > UPDATE Stock SET Stock.Quantity =3D Stock.Quantity - 1 WHERE Stock.Produc= tID =3D 1 AND Stock.SizeID =3D 1; > COMMIT; > > Session 2 query > START TRANSACTION; > SET AUTOCOMMIT=3D0; > SELECT Stock.Quantity FROM Stock WHERE Stock.ProductID =3D 1 AND Stock.Si= zeID =3D 2 FOR UPDATE; > UPDATE Stock SET Stock.Quantity =3D Stock.Quantity - 1 WHERE Stock.Produc= tID =3D 1 AND Stock.SizeID =3D 2; > COMMIT; > > 1) Session 1: START TRANSACTION; > 2) Session 1: SET AUTOCOMMIT=3D0; > 3) Session 1: SELECT Stock.Quantity FROM Stock WHERE Stock.ProductID =3D = 1 AND Stock.SizeID =3D 1 FOR UPDATE; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(returns Stock.Quantity as expecte= d) > 4) Session 2: START TRANSACTION; > 5) Session 2: SET AUTOCOMMIT=3D0; > 6) Session 2: SELECT Stock.Quantity FROM Stock WHERE Stock.ProductID =3D = 1 AND Stock.SizeID =3D 2 FOR UPDATE; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(blocks, but this is not exepected= and is unwanted behaviour, because session 2 is using another row) > 7) Session 1: UPDATE Stock SET Stock.Quantity =3D Stock.Quantity - 1 WHER= E Stock.ProductID =3D 1 AND Stock.SizeID =3D 1; > 8) Session 1: COMMIT; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(the blocking step 6 is now execut= ed and returns the Stock.Quantity as exepected) > 9) Session 2: UPDATE Stock SET Stock.Quantity =3D Stock.Quantity - 1 WHER= E Stock.ProductID =3D 1 AND Stock.SizeID =3D 2; > 10) Session 2: COMMIT; > > Regards, > > Johan Machielse > Machielse Software > http://www.machielsesoftware.nl --=20 Baron Schwartz Percona Inc: Services and Support for MySQL http://www.percona.com/