猫猫1 发表于 2018-9-29 11:23:26

MySQL一次死锁的问题

  一个线上系统,稳定运行了一段时间后突然出现一些死锁情况,程序抛异常类似于下面这样:
  


[*]com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
[*]      at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[*]      at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
[*]      at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
[*]      at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
[*]      at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
[*]      at com.mysql.jdbc.Util.getInstance(Util.java:381)
[*]      at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1045)
[*]      at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
[*]      at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3491)
[*]      at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3423)
  

  导致出现问题的SQL是这样的
  


[*]insert into xxx (...)
[*]    select aaa, bbb from dual where not exists (
[*]      select * from xxx where aaa = yyy
[*]    )
  

  这是一个我们很常见的场景:先查下某条数据是否存在,若不存在就插入一条。
  之所以sql这样写,是为了防止高并发情况下发生唯一性约束冲突的异常,这种先select再insert的写法,被称作conditional insert。这里有一个潜在问题就是内层select会加锁,如果此时另外一个线程也进行查询操作,会直接deadlock。
  解决办法也很粗暴:
  直接insert,不带任何条件,然后程序里面做下容错,将所有唯一性约束的异常吃掉。
  MySQL里面有很多这样看上去很美的写法和功能,真正用了才发现自己掉坑里了。生产环境,还是保守一点好。


页: [1]
查看完整版本: MySQL一次死锁的问题