当前位置: 首页>编程语言>正文

Java mysql删除大量数据导致表锁死 mysql删除数据会锁表吗

之前有一个同事问到我,为什么多个线程同时去做删除同一行数据的操作,老是报死锁,在线上已经出现好多次了,我问了他几个问题:

1. 是不是在一个事务中做了好几件事情?

答:不是,只做一个删除操作,自动提交

2. 有多少个线程在做删除?

答:差不多10个

3. 是什么隔离级别?

答:可重复读

当时觉得不可思议,按说自动提交的话行锁,如果已经有事务加锁了,则会等待,等提交之后再去做,发现已经删除了,就会返回,删除0条,为什么会死锁?

但事情已经出了,必须研究一下,不然终究是心头之苦啊。

然后想到既然线上有这么简单的操作就可以死锁,那么应该写个简单的程序就可以重现,然后同事李润杰兄弟咔嚓咔嚓没多时就给我了,在这里谢谢他。

首先环境是这样的:

CREATE TABLE `abcdefg` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`abc` varchar(30),
`def` varchar(30) ,
`ghi` date,
`jkl` date,
`mnp` tinyint(4),
PRIMARY KEY (`id`),
UNIQUE KEY `uniqdefghijkl` (`def`,`ghi`,`jkl`)
);

这个表包括2个索引,一个是聚簇索引,另一个是uniqdefghijkl的二级唯一索引。

事先插入很多数据,然后3个线程同时做对同一条记录的删除,这里只做删除操作,并且都是自动提交,为了得到一批要删除的数据,事先查询很多条出来备用。

删除语句是这样的:

delete from abcdefg WHERE abc= '我是变量' and def= '我是变量' and ghi= '2013-12-19 00:00:00' and jkl= '2013-12-20 00:00:00';

那么现在就开始重现。。。

果然很快,死锁真的出现了,下面是执行show engine innodb status的结果:

===================================================

LATEST DETECTED DEADLOCK

------------------------

140123 12:20:50

*** (1) TRANSACTION:

TRANSACTION 2E10, ACTIVE 4917 sec starting index read

mysql tables in use 1, locked 1

LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s)

MySQL thread id 3, OS thread handle 0x1008, query id 43 192.168.xx.x username upd

ating

delete from abcdefg WHERE abc= '我是变量' and def= '我是变量' and ghi= '2013-12-19 00:00:00' and jkl= '2013-12-20 00:00:00';

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 0 page no 12295 n bits 528 index `uniqdefghijkl` of table

`deadlock`.`abcdefg` trx id 2E10 lock_mode X locks rec but not gap waiti

ng

Record lock, heap no 167 PHYSICAL RECORD: n_fields 4; compact format;

*** (2) TRANSACTION:

TRANSACTION 2E0E, ACTIVE 4917 sec starting index read

mysql tables in use 1, locked 1

3 lock struct(s), heap size 1248, 2 row lock(s)

MySQL thread id 1, OS thread handle 0x1190, query id 41 192.168.xx.xx username upd

ating

delete from abcdefg WHERE abc= '我是变量' and def= '我是变量' and ghi= '2013-12-19 00:00:00' and jkl= '2013-12-20 00:00:00';

*** (2) HOLDS THE LOCK(S):

RECORD LOCKS space id 0 page no 12295 n bits 528 index `uniqdefghijkl` of table

`deadlock`.`abcdefg` trx id 2E0E lock_mode X locks rec but not gap

Record lock, heap no 167 PHYSICAL RECORD: n_fields 4; compact format;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 0 page no 12295 n bits 528 index `uniqdefghijkl` of table

`deadlock`.`abcdefg` trx id 2E0E lock_mode X waiting

Record lock, heap no 167 PHYSICAL RECORD: n_fields 4; compact format;

*** WE ROLL BACK TRANSACTION (1)

===================================================

这是在三个线程的情况下是可以重现死锁的,但是为了更容易调试,试了一下在2个线程的情况下如何,最终发现重现不了。

这下坏了,多线程调试很麻烦,有时候这个走那个不走的,如果冻结某个线程,有可能导致线程之间死锁,或者自然执行,那又不能出现死锁的情况,因为这个死锁也是偶然性的,所以最终只有一种方法,那就是在mysql代码中打印log信息,将锁、记录与事务这块的函数中具有分歧点的地方都加了注释,并且将有用的信息打印出来,最终分析log文件,才发现了真正死锁的猫腻。

现在将三个导致死锁的事务的时序图画出来:


https://www.xamrdz.com/lan/5bh1930505.html

相关文章: