Reputation: 35
I receive data from a remote server via HTTP POST requests in JSON format which I save for later processing in a database table. Occasionally I encounter deadlock errors doing so. The data is unique per an identifier, so before inserting a new data set, I delete any old ones already present in the table. To avoid data loss in this process, I use a transaction, so the queries look something like this:
BEGIN TRANSACTION;
DELETE FROM ChangeEvent WHERE rId = 'someid';
INSERT INTO ChangeEvent (records, rId) VALUES ('...', 'someid');
COMMIT;
The ChangeEvent
table has an index on an auto-increment id column and another index on the rId
column.
During the insertion, this sometimes causes a deadlock, where it appears that two datasets for the same id get inserted at the same time.
------------------------
LATEST DETECTED DEADLOCK
------------------------
2024-06-11 13:59:38 0x7f9a6804a700
*** (1) TRANSACTION:
TRANSACTION 7915, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 1
MySQL thread id 20, OS thread handle 140301147133696, query id 35 172.20.0.4 db3 Update
INSERT INTO `ChangeEvent` (`records`, `rId`)
VALUES ('[]', '153181')
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 256 page no 4 n bits 72 index rId of table `usr`.`ChangeEvent` trx id 7915 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) TRANSACTION:
TRANSACTION 7916, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 1
MySQL thread id 21, OS thread handle 140301146826496, query id 36 172.20.0.4 db3 Update
INSERT INTO `ChangeEvent` (`records`, `rId`)
VALUES ('...', '153181')
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 256 page no 4 n bits 72 index rId of table `usr`.`ChangeEvent` trx id 7916 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 256 page no 4 n bits 72 index rId of table `usr`.`ChangeEvent` trx id 7916 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** WE ROLL BACK TRANSACTION (2)
From my understanding of transactions and deadlocks, this shouldn't be happening. As far as I can tell, it seems the issue is connected to the rId
index, as without it, the issue stops happening, however, I fear that the DELETE
query could slow down significantly without the index, as occasionally the number of datasets in the table could go up to ten thousand.
My questions are
Upvotes: 0
Views: 100
Reputation: 142503
REPLACE INTO ChangeEvent (records, rId) VALUES ('...', 'someid');
Is an atomic DELETE
+ INSERT
. Yes, it will create an new auto_inc id.
Upvotes: 0
Reputation: 1348
Why not get ride of explicit transaction, using ON DUPLICATE KEY ? like:
INSERT INTO ChangeEvent (records, rId) VALUES ('...', 'someid') ON DUPLICATE KEY UPDATE records='...';
This will insert data, or if keys already exists, update value.
Upvotes: 2
Reputation: 1949
You could try locking, like this, so only one process is allowed to execute those lines at one time. (This is untested.) The "30" is the number of seconds to wait for the lock to be available if another process has it.
SELECT GET_LOCK('mylock', 30);
BEGIN TRANSACTION;
DELETE FROM ChangeEvent WHERE rId = 'someid';
INSERT INTO ChangeEvent (records, rId) VALUES ('...', 'someid');
COMMIT;
SELECT RELEASE_LOCK('mylock');
Also see documentation here: https://mariadb.com/kb/en/get_lock/
Upvotes: -1