博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Deadlock found when trying to get lock,mysql死锁
阅读量:4230 次
发布时间:2019-05-26

本文共 10309 字,大约阅读时间需要 34 分钟。

最近遇到一个死锁问题,我整理了一下,写了一个例子。

初始化表

国籍表:

CREATE TABLE `nationality` (`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,`type` VARCHAR ( 64 ) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`desc` VARCHAR ( 255 ) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`count` INT ( 11 ) DEFAULT NULL,PRIMARY KEY ( `id` ) ) ENGINE = INNODB AUTO_INCREMENT = 2 DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;

用户表:

CREATE TABLE `person` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,  `desc` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,  `nationality_id` int(11) NOT NULL,  PRIMARY KEY (`id`),  KEY `nationality_fk` (`nationality_id`),  CONSTRAINT `nationality_fk` FOREIGN KEY (`nationality_id`) REFERENCES `nationality` (`id`)) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
功能代码

下面是一个很简单的功能。

功能如下:新增一个用户,并且往把该用户所在国籍的人数加1。

新增国籍人数的sql:

update nationality set `count` = `count` + #{increase} where id = #{id}

创建用户的sql:

insert into person (`name`,`desc`,`nationality_id`) values( #{name}, #{desc}, #{nationalityId})

创建用户的service代码:

@Servicepublic class PersonService {    private static final Integer CHINA_ID = 1;    @Autowired    private PersonMapper personMapper;    @Autowired    private NationalityMapper nationalityMapper;    @Transactional    public void create(String name, String desc) {        Person person = new Person();        person.setName(name);        person.setDesc(desc);        person.setNationalityId(CHINA_ID);        personMapper.create(person);        try {            //这里加了一个延迟,方便测试            Thread.sleep(5000L);        } catch (Exception e) {            throw new RuntimeException(e);        }        nationalityMapper.updateCount(CHINA_ID, 1);    }}
测试

模拟并发创建用户:

@SpringBootApplication@MapperScan("com.example.bootmybatis.dao")public class BootmybatisApplication {    public static void main(String[] args) {        ApplicationContext applicationContext = SpringApplication.run(BootmybatisApplication.class, args);        PersonService personService = applicationContext.getBean(PersonService.class);        new Thread(() -> personService.create("测试1", "无")).start();        new Thread(() -> personService.create("测试2", "无")).start();    }}

控制台报错如下:

Exception in thread "Thread-155" org.springframework.dao.DeadlockLoserDataAccessException: ### Error updating database.  Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction### The error may involve defaultParameterMap### The error occurred while setting parameters### SQL: update nationality         set `count` = `count` + ?         where id = ?### Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction; Deadlock found when trying to get lock; try restarting transaction; nested exception is com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction	at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:267)	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73)	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)	at com.sun.proxy.$Proxy57.insert(Unknown Source)	at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:278)	at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:58)	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)	at com.sun.proxy.$Proxy59.updateCount(Unknown Source)	at com.example.bootmybatis.service.PersonService.create(PersonService.java:37)	at com.example.bootmybatis.service.PersonService$$FastClassBySpringCGLIB$$c378fac9.invoke(
) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691) at com.example.bootmybatis.service.PersonService$$EnhancerBySpringCGLIB$$85b28c3f.create(
) at com.example.bootmybatis.BootmybatisApplication.lambda$main$0(BootmybatisApplication.java:16) at java.lang.Thread.run(Thread.java:745)Caused by: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:123) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953) at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:370) at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3051) at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_execute(FilterEventAdapter.java:440) at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3049) at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.execute(PreparedStatementProxyImpl.java:167) at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:498) at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:46) at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:74) at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:50) at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117) at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:63) at com.sun.proxy.$Proxy69.update(Unknown Source) at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:198) at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:185) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433) ... 19 more

查看数据库只有demo2创建成功了。

查看mysql死锁日志如下,(可以通过SHOW ENGINE INNODB STATUS 查看最近死锁日志)

------------------------LATEST DETECTED DEADLOCK------------------------2020-06-11 15:55:25 0x3d8c*** (1) TRANSACTION:TRANSACTION 164396, ACTIVE 5 sec starting index readmysql tables in use 1, locked 1LOCK WAIT 5 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1MySQL thread id 20, OS thread handle 17124, query id 341 localhost 127.0.0.1 root updatingupdate nationality        set `count` = `count` + 1        where id = 1*** (1) WAITING FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 436 page no 3 n bits 72 index PRIMARY of table `boot`.`nationality` trx id 164396 lock_mode X locks rec but not gap waitingRecord lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0 0: len 4; hex 80000001; asc     ;; 1: len 6; hex 00000002822a; asc      *;; 2: len 7; hex 3f000001f521c4; asc ?    ! ;; 3: len 5; hex 4348494e41; asc CHINA;; 4: len 6; hex e4b8ade59bbd; asc       ;; 5: len 4; hex 80000002; asc     ;;*** (2) TRANSACTION:TRANSACTION 164397, ACTIVE 5 sec starting index read, thread declared inside InnoDB 5000mysql tables in use 1, locked 15 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1MySQL thread id 21, OS thread handle 15756, query id 340 localhost 127.0.0.1 root updatingupdate nationality        set `count` = `count` + 1        where id = 1*** (2) HOLDS THE LOCK(S):RECORD LOCKS space id 436 page no 3 n bits 72 index PRIMARY of table `boot`.`nationality` trx id 164397 lock mode S locks rec but not gapRecord lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0 0: len 4; hex 80000001; asc     ;; 1: len 6; hex 00000002822a; asc      *;; 2: len 7; hex 3f000001f521c4; asc ?    ! ;; 3: len 5; hex 4348494e41; asc CHINA;; 4: len 6; hex e4b8ade59bbd; asc       ;; 5: len 4; hex 80000002; asc     ;;*** (2) WAITING FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 436 page no 3 n bits 72 index PRIMARY of table `boot`.`nationality` trx id 164397 lock_mode X locks rec but not gap waitingRecord lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0 0: len 4; hex 80000001; asc     ;; 1: len 6; hex 00000002822a; asc      *;; 2: len 7; hex 3f000001f521c4; asc ?    ! ;; 3: len 5; hex 4348494e41; asc CHINA;; 4: len 6; hex e4b8ade59bbd; asc       ;; 5: len 4; hex 80000002; asc     ;;*** WE ROLL BACK TRANSACTION (2)

根据日志可以看到事务1在等待获取X锁,事务2持有S锁,在等待X锁。其实事务1和事务2都持有S锁,都在等待X锁,因为两个事务S锁都不释放,所以

都无法获取X锁,所以就死锁了。删除外键后就没有问题了。

转载地址:http://lsjqi.baihongyu.com/

你可能感兴趣的文章
Beginning ASP.NET 2.0 Databases: From Novice to Professional
查看>>
Microsoft Windows 2000 Scripting Guide
查看>>
Visual Basic .NET Bible
查看>>
The Struts Framework: Practical Guide for Java Programmers
查看>>
C#.net Web Developer's Guide
查看>>
Virtualization with VMware ESX Server
查看>>
System Architecture with XML
查看>>
Microsoft SQL Server 2005 Stored Procedure Programming in T-SQL & .NET
查看>>
Sams Teach Yourself Microsoft Office Access 2003 in 24 Hours
查看>>
Leveraging Web Services: Planning, Building, and Integration for Maximum Impact
查看>>
Implementing Backup and Recovery: The Readiness Guide for the Enterprise
查看>>
Wireless Communications over MIMO Channels: Applications to CDMA and Multiple Antenna Systems
查看>>
UMTS Performance Measurement: A Practical Guide to KPIs for the UTRAN Environment
查看>>
Grid Networks: Enabling Grids with Advanced Communication Technology
查看>>
Communication Systems for the Mobile Information Society
查看>>
Beginning Ubuntu Linux: From Novice to Professional
查看>>
IPsec Virtual Private Network Fundamentals
查看>>
Algorithms and Networking for Computer Games
查看>>
Java Regular Expressions: Taming the java.util.regex Engine
查看>>
CSS Instant Results
查看>>