使用多线程代码在一张表上发生 MySQL 死锁

Posted

技术标签:

【中文标题】使用多线程代码在一张表上发生 MySQL 死锁【英文标题】:MySQL deadlock on one table with multithreading code 【发布时间】:2016-03-16 22:46:45 【问题描述】:

我创建了一个收集价格并将其存储在数据库中的应用程序。 该应用程序使用两个线程运行。但时不时会出现死锁。我认为这是因为踏板将大量价格插入到同一张表中。

桌子

[tripid | date | duration | price | garant | updatetime | persons | accommodationid]

索引

unique (tripid, date, duration, price, persons) foreign key 'accommodationid' to table 'accommodation.id'

查询

insert into prices (tripid, date, duration, price, persons, accomodationid) values ( 1 , 2016-6-4, 8, 200,2,32), ... a whole lot more ... ( 1 , 2016-7-4, 8, 200,2,32) on duplicate key update price = values(price);

因为可以在 1 个查询中插入 1000 个值,所以需要一些时间。我认为当第二个线程想要插入另外 1000 个值但第一个未完成时会发生死锁。

innodb 状态

------------------------
LATEST DETECTED DEADLOCK
------------------------
2016-03-16 23:13:13 0xef8
*** (1) TRANSACTION:
TRANSACTION 3732195928, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 1
MySQL thread id 415, OS thread handle 7328, query id 10956855 ---(ip)-- --name-- update
insert into `prices` (tripid,`date`,`duration`,`price`,`garant`,`updatetime`,`persons`,`accommodationid`) values
(179881,'2016-03-18',2,206,'0','2016-03-16','1','119'),(179881,'2016-03-18',3,260,'0','2016-03-16','1','119'),(179881,'2016-03-18',4,313,'0','2016-03-16','1','119'),(179881,'2016-03-18',5,366,'0','2016-03-16','1','119'),(179881,'2016-03-19',2,206,'0','2016-03-16','1','119'),(179881,'2016-03-19',3,260,'0','2016-03-16','1','119'),(179881,'2016-03-19',4,313,'0','2016-03-16','1','119'),(179881,'2016-03-19',5,366,'0','2016-03-16','1','119'),(179881,'2016-03-20',2,206,'0','2016-03-16','1','119'),(179881,'2016-03-20',3,260,'0','2016-03-16','1','119'),(179881,'2016-03-20',4,313,'0','2016-03-16','1','119'),(179881,'2016-03-20',5,366,'0','2016-03-16','1','119'),(179881,'2016-03-21',2,206,'0','2016-03-16','1','119'),(179881,'2016-03-21',3,260,'0','2016-03-16','1','119'),(179881,'2016-03-21',4,313,
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 247 page no 1457 n bits 136 index keyGroup of table `travel`.`prices` trx id 3732195928 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 3732195927, ACTIVE 0 sec inserting, thread declared inside InnoDB 4529
mysql tables in use 1, locked 1
15 lock struct(s), heap size 1136, 476 row lock(s), undo log entries 472
MySQL thread id 414, OS thread handle 3832, query id 10956852 ---(ip)-- --name-- update
insert into `prices` (tripid,`date`,`duration`,`price`,`garant`,`updatetime`,`persons`,`accommodationid`) values
(179880,'2016-03-18',2,185,'0','2016-03-16','1','1438'),(179880,'2016-03-18',3,217,'0','2016-03-16','1','1438'),(179880,'2016-03-18',4,249,'0','2016-03-16','1','1438'),(179880,'2016-03-18',5,279,'0','2016-03-16','1','1438'),(179880,'2016-03-18',6,312,'0','2016-03-16','1','1438'),(179880,'2016-03-18',7,343,'0','2016-03-16','1','1438'),(179880,'2016-03-18',8,375,'0','2016-03-16','1','1438'),(179880,'2016-03-19',2,185,'0','2016-03-16','1','1438'),(179880,'2016-03-19',3,217,'0','2016-03-16','1','1438'),(179880,'2016-03-19',4,249,'0','2016-03-16','1','1438'),(179880,'2016-03-19',5,279,'0','2016-03-16','1','1438'),(179880,'2016-03-19',6,312,'0','2016-03-16','1','1438'),(179880,'2016-03-19',7,343,'0','2016-03-16','1','1438'),(179880,'2016-03-20',2,185,'0','2016-03-16','1','1438'),(179880,'2016
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 247 page no 1457 n bits 72 index keyGroup of table `travel`.`prices` trx id 3732195927 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 247 page no 1457 n bits 136 index keyGroup of table `travel`.`prices` trx id 3732195927 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 (1)
------------

问题

如何避免死锁?

【问题讨论】:

通过使用单个线程,至少用于插入/更新。 该应用程序的功能更多。如果我在应用程序中使用更多线程,我可以将计算价格的整个过程缩短 4 小时。所以说“使用 1 个线程”是不是有点简单? 【参考方案1】:

我似乎已经通过首先锁定表来修复它,然后执行大量查询并解锁它,以便第二个线程获得访问权限。

LOCK TABLES prices WRITE;

insert into prices (tripid, date, duration, price, persons, accomodationid)
            values  ( 1 , 2016-6-4, 8, 200,2,32),
                       ... a whole lot more ... 
                    ( 1 , 2016-7-4, 8, 200,2,32)
            on duplicate key update price = values(price);

UNLOCK TABLES;

【讨论】:

这有效地使应用程序成为单线程,因为在任何时候,最多一个线程处于活动状态,而其他线程在等待。 (车队锁) 嗯.. 这不会使应用程序成为单线程,只会执行此查询。该应用程序会执行大量其他操作,并且每 10 分钟一次,它们会同时执行此查询。所以如果等待另一个线程完成这个查询也没问题。

以上是关于使用多线程代码在一张表上发生 MySQL 死锁的主要内容,如果未能解决你的问题,请参考以下文章

如何在一张表上查找不同 ID 的数量

如何解决多线程造成的数据库死锁

MYSQL 将两张表整合在一张表中,不会遗漏任何一行

如何在一张表中添加两个查询?

SQL问题,怎么将一张表的某个字段更新为另一张表的字段

更新查询在同一张表上的 Sql 查询死锁