简体   繁体   中英

Howto bulk update a InnoDB table without deadlocks?

I have two tables, one having a many-to-many relationship ( fooBarTable with columns fooId and barId ) and another InnoDB table fooCounterTable with columns fooId and counter counting the occurences of fooId in fooBarTable .

When deleting all barId 's from fooBarTable , I need to update the fooCounterTable accordingly.

The first thing I tried was this:

UPDATE fooCounterTable SET counter = counter - 1
    WHERE fooId IN (SELECT fooId FROM fooBarTable WHERE barId = 42 ORDER BY fooId);

But I got this error:

MySQL error (1205): Lock wait timeout exceeded; try restarting transaction

Updating the table when adding barId 's is working fine with this SQL statement:

INSERT INTO `fooCounterTable` (fooId, counter) VALUES (42,1), (100,1), (123,1)
    ON DUPLICATE KEY UPDATE counter = counter + 1;

So I thought I'd do the same thing when decreasing the counter, even if it looks stupid to insert 0-Values, which should never happen:

INSERT INTO `fooCounterTable` (SELECT fooId, 0 FROM fooBarTable WHERE barId = 42 ORDER BY fooId)
    ON DUPLICATE KEY UPDATE counter = counter - 1;'

This seems to work fine in most cases, but sometimes I get a deadlock:

MySQL error (1213): Deadlock found when trying to get lock; try restarting transaction

So I read about deadlocks and found out about SELECT ... FOR UPDATE and I tried this:

START TRANSACTION;
SELECT fooId FROM fooCounterTable 
    WHERE fooId IN (SELECT fooId FROM fooBarTable WHERE barId = 42 ORDER BY fooId) FOR UPDATE;
UPDATE fooCounterTable SET counter = counter - 1
    WHERE fooId IN (SELECT fooId FROM fooBarTable WHERE barId = 42 ORDER BY fooId);
COMMIT;

which resulted in:

MySQL error (2014): commands out of sync

Can anyone tell me how to resolve my problem?

Update

The last error (2014) occured, because I did not use and free the SELECT statement's results before executing the UPDATE statement, which is mandatory. I fixed that and I got rid of error 2014, but I still have deadlocks (error 1205) from time to time and I don't understand, why.

Do you know what fooID you have just deleted when firing this query?

If so seems like this would work...

UPDATE fooCounterTable SET counter = 
  (SELECT count(*) FROM fooBarTable WHERE fooId = 42) 
  WHERE fooID = 42

I wonder if you really need that counter table tho. If your indexes are set up properly there shouldn't be too much of a speed penalty to a more normalized approach.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM