使用的mysql 的 REPLACE INTO 多线程并发处理同一张表导致表死锁。
Mysql锁类型分析
MySQL有三种锁的级别:页级、表级、行级,这个地方我遇到的问题是来自于行级锁,所以重点说一下。
行级锁在使用的时候并不是直接锁掉这行记录,而是锁索引
如果一条sql用到了主键索引(mysql主键自带索引),mysql会锁住主键索引;
如果一条sql操作了非主键索引,mysql会先锁住非主键索引,再锁定主键索引.
死锁原理
mysql的两种锁排它锁(X锁)和共享锁(S锁)(mysql还有其他锁,需要了解可以自己去查,这个地方列举两个):
X锁,是事务T对数据A加上X锁时,只允许事务T读取和修改数据A,别的事务就没办法读取和修改,所以也叫排它锁,是互斥的
S锁,是事务T对数据A加上S锁时,其他事务只能再对数据A加S锁,而不能加X锁,直到T释放A上的S锁,别的事务也用加S锁,所以也叫共享锁,是不互斥的
一般造成死锁的原因是因为两个事物添加锁的时候没能及时的解锁释放资源,等到第二个事务要添加锁的时候发现已经被锁,从而造成环路等待,构成死锁条件。
Replace INTO 这个语法的执行过程和原理
Replace INTO 插入数据前会根据Mysql表中的索引字段,去查询有没有和你插入数据的索引字段有重复,
如果有就先将该数据删除在插入新数据。
再删除和插入的过程中就涉及到了一个行级锁问题,这个过程是有事物存在的,
也就是说要么成功更新数据,要么老数据不变,不能发生老的数据删除但是新的数据未插入这种现象
在删除数据之前首先获得这一行数据的锁,然后在做删除和插入的操作,直到操作结束释放该锁
我的程序是多线程并发 Replace INTO同一张表,表有唯一索引,Replace INTO 查询过程加S锁,删除和插入过程加X锁。
有2个线程,
线程1的X锁正准备加上还是还没加上,实际是存在X锁,但是线程2加了s锁,线程1会等待线程2的s锁 线程2的完整事务加了s锁立即就要加x锁,但是线程1的x锁没有释放。造成了环路等待。
解决方法
进行update、delete的时候尽量避开非主键索引,我这里记录一下被锁后应该怎么去解决的方法,首先先用sql查询一下mysql的事务处理表
select * from information_schema.INNODB_TRX
正常情况下的状态都是RUNNING,但是在被锁之后就会变成LOCK WAIT ,一旦出现这种情况,就得杀死这个进程,如果进程杀不死就只能重启Mysql服务了
杀死进程
kill 进程ID
经验教训
无论前台后台的程序,都不应该存在仅根据非主键的几个字段一查就要update/delete的场景。即使有,也应该改为先把要更新的记录查出来然后逐条按主键id更新。