I have a innoDB table which records online users. It gets updated on every page refresh by a user to tướng keep track of which pages they are on and their last access date to tướng the site. I then have a cron that runs every 15 minutes to tướng DELETE old records.
I got a 'Deadlock found when trying to tướng get lock; try restarting transaction' for about 5 minutes last night and it appears to tướng be when running INSERTs into this table. Can someone suggest how to tướng avoid this error?
=== EDIT ===
Here are the queries that are running:
First Visit to tướng site:
INSERT INTO onlineusers SET
ip = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3
On each page refresh:
UPDATE onlineusers SET
ips = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3
WHERE id = 888
Cron every 15 minutes:
DELETE FROM onlineusers WHERE datetime <= now() - INTERVAL 900 SECOND
It then does some counts to tướng log some stats (ie: members online, visitors online).
Lii
12.1k9 gold badges68 silver badges89 bronze badges
asked Feb 25, 2010 at 9:03
DavidDavid
16.7k35 gold badges109 silver badges168 bronze badges
3
One easy trick that can help with most deadlocks is sorting the operations in a specific order.
You get a deadlock when two transactions are trying to tướng lock two locks at opposite orders, ie:
- connection 1: locks key(1), locks key(2);
- connection 2: locks key(2), locks key(1);
If both lập cập at the same time, connection 1 will lock key(1), connection 2 will lock key(2) and each connection will wait for the other to tướng release the key -> deadlock.
Now, if you changed your queries such that the connections would lock the keys at the same order, ie:
- connection 1: locks key(1), locks key(2);
- connection 2: locks key(1), locks key(2);
it will be impossible to tướng get a deadlock.
So this is what I suggest:
Make sure you have no other queries that lock access more than vãn one key at a time except for the delete statement. if you vì thế (and I suspect you do), order their WHERE in (k1,k2,..kn) in ascending order.
Fix your delete statement to tướng work in ascending order:
Change
DELETE FROM onlineusers
WHERE datetime <= now() - INTERVAL 900 SECOND
To
DELETE FROM onlineusers
WHERE id IN (
SELECT id FROM onlineusers
WHERE datetime <= now() - INTERVAL 900 SECOND
ORDER BY id
) u;
Another thing to tướng keep in mind is that MySQL documentation suggest that in case of a deadlock the client should retry automatically. you can add this logic to tướng your client code. (Say, 3 retries on this particular error before giving up).
Lii
12.1k9 gold badges68 silver badges89 bronze badges
answered Mar 11, 2010 at 9:48
Omry YadanOmry Yadan
33.5k19 gold badges71 silver badges89 bronze badges
14
Deadlock happen when two transactions wait on each other to tướng acquire a lock. Example:
- Tx 1: lock A, then B
- Tx 2: lock B, then A
There are numerous questions and answers about deadlocks. Each time you insert/update/or delete a row, a lock is acquired. To avoid deadlock, you must then make sure that concurrent transactions don't update row in an order that could result in a deadlock. Generally speaking, try to tướng acquire lock always in the same order even in different transaction (e.g. always table A first, then table B).
Another reason for deadlock in database can be missing indexes. When a row is inserted/update/delete, the database needs to tướng kiểm tra the relational constraints, that is, make sure the relations are consistent. To vì thế so sánh, the database needs to tướng kiểm tra the foreign keys in the related tables. It might result in other lock being acquired than vãn the row that is modified. Be sure then to tướng always have index on the foreign keys (and of course primary keys), otherwise it could result in a table lock instead of a row lock. If table lock happen, the lock contention is higher and the likelihood of deadlock increases.
fedorqui
288k108 gold badges584 silver badges626 bronze badges
answered Feb 25, 2010 at 9:21
ewernliewernli
38.6k5 gold badges94 silver badges123 bronze badges
4
In case someone is still struggling with this issue:
I faced similar issue where 2 requests were hitting the server at the same time. There was no situation lượt thích below:
T1:
BEGIN TRANSACTION
INSERT TABLE A
INSERT TABLE B
END TRANSACTION
T2:
BEGIN TRANSACTION
INSERT TABLE B
INSERT TABLE A
END TRANSACTION
So, I was puzzled why deadlock is happening.
Then I found that there was parent child relation ship between 2 tables because of foreign key. When I was inserting a record in child table, the transaction was acquiring a lock on parent table's row. Immediately after that I was trying to tướng update the parent row which was triggering elevation of lock to tướng EXCLUSIVE one. As 2nd concurrent transaction was already holding a SHARED lock, it was causing deadlock.
Refer to: https://blog.tekenlight.com/2019/02/21/database-deadlock-mysql.html
answered Feb 26, 2019 at 13:41
chatsapchatsap
6816 silver badges5 bronze badges
2
It is likely that the delete statement will affect a large fraction of the total rows in the table. Eventually this might lead to tướng a table lock being acquired when deleting. Holding on to tướng a lock (in this case row- or page locks) and acquiring more locks is always a deadlock risk. However I can't explain why the insert statement leads to tướng a lock escalation - it might have to tướng vì thế with page splitting/adding, but someone knowing MySQL better will have to tướng fill in there.
For a start it can be worth trying to tướng explicitly acquire a table lock right away for the delete statement. See LOCK TABLES and Table locking issues.
informatik01
16.4k11 gold badges78 silver badges108 bronze badges
answered Mar 6, 2010 at 19:42
Anders AbelAnders Abel
69.2k18 gold badges153 silver badges221 bronze badges
You might try having that delete
job operate by first inserting the key of each row to tướng be deleted into a temp table lượt thích this pseudocode
create temporary table deletetemp (userid int);
insert into deletetemp (userid)
select userid from onlineusers where datetime <= now - interval 900 second;
delete from onlineusers where userid in (select userid from deletetemp);
Breaking it up lượt thích this is less efficient but it avoids the need to tướng hold a key-range lock during the delete
.
Also, modify your select
queries to tướng add a where
clause excluding rows older than vãn 900 seconds. This avoids the dependency on the cron job and allows you to tướng reschedule it to tướng lập cập less often.
Theory about the deadlocks: I don't have a lot of background in MySQL but here goes... The delete
is going to tướng hold a key-range lock for datetime, to tướng prevent rows matching its where
clause from being added in the middle of the transaction, and as it finds rows to tướng delete it will attempt to tướng acquire a lock on each page it is modifying. The insert
is going to tướng acquire a lock on the page it is inserting into, and then attempt to tướng acquire the key lock. Normally the insert
will wait patiently for that key lock to tướng open up but this will deadlock if the delete
tries to tướng lock the same page the insert
is using because thedelete
needs that page lock and the insert
needs that key lock. This doesn't seem right for inserts though, the delete
and insert
are using datetime ranges that don't overlap so sánh maybe something else is going on.
http://dev.mysql.com/doc/refman/5.1/en/innodb-next-key-locking.html
answered Mar 11, 2010 at 16:26
For Java programmers using Spring, I've avoided this problem using an AOP aspect that automatically retries transactions that lập cập into transient deadlocks.
See @RetryTransaction Javadoc for more info.
answered Jun 29, 2013 at 14:59
ArchieArchie
5,3411 gold badge33 silver badges38 bronze badges
@Omry Yadan's answer ( https:///a/2423921/1810962 ) can be simplified by using ORDER BY.
Change
DELETE FROM onlineusers
WHERE datetime <= now() - INTERVAL 900 SECOND
to
DELETE FROM onlineusers
WHERE datetime <= now() - INTERVAL 900 SECOND
ORDER BY ID
to keep the order in which you delete items consistent. Also if you are doing multiple inserts in a single transaction, make sure they are also always ordered by id.
According to tướng the mysql delete documentation:
If the ORDER BY clause is specified, the rows are deleted in the order that is specified.
You can find a reference here: https://dev.mysql.com/doc/refman/8.0/en/delete.html
answered Aug 10, 2021 at 16:28
josephjoseph
2,7761 gold badge30 silver badges51 bronze badges
1
I have a method, the internals of which are wrapped in a MySqlTransaction.
The deadlock issue showed up for má when I ran the same method in parallel with itself.
There was not an issue running a single instance of the method.
When I removed MySqlTransaction, I was able to tướng lập cập the method in parallel with itself with no issues.
Just sharing my experience, I'm not advocating anything.
answered Aug 4, 2018 at 13:29
BitsAndBytesBitsAndBytes
8651 gold badge9 silver badges13 bronze badges
1
cron
is dangerous. If one instance of cron fails to tướng finish before the next is due, they are likely to tướng fight each other.
It would be better to tướng have a continuously running job that would delete some rows, sleep some, then repeat.
Also, INDEX(datetime)
is very important for avoiding deadlocks.
But, if the datetime test includes more than vãn, say, 20% of the table, the DELETE
will vì thế a table scan. Smaller chunks deleted more often is a workaround.
Another reason for going with smaller chunks is to tướng lock fewer rows.
Bottom line:
INDEX(datetime)
- Continually running task -- delete, sleep a minute, repeat.
- To make sure that the above task has not died, have a cron job whose sole purpose is to tướng restart it upon failure.
Other deletion techniques: http://mysql.rjweb.org/doc.php/deletebig
answered Dec 5, 2019 at 21:27
Rick JamesRick James
142k14 gold badges138 silver badges242 bronze badges