update语句时造成的血案,报Lock wait timeout exceeded; try restarting transaction

最近查看后台日志,发现出现了大批量的锁表日志,顿时产生好多不解,报错日志如下:

### Error updating database.  Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
### The error may involve com.xes.payplatform.ledger.common.dao.WechatpayTradeDataMapper.updateByConditionSelective-Inline
### The error occurred while setting parameters
### SQL: update pp_wechatpay_trade_data      SET status = ?, remarks = ?, modify_time = ?, checking_time = ?  WHERE (  status = ?                                                                and area_code = ?  and deleted = ? )
### Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
; SQL []; Lock wait timeout exceeded; try restarting transaction; nested exception is com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
	at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:259)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
	at com.sun.proxy.$Proxy85.update(Unknown Source)
	at org.mybatis.spring.SqlSessionTemplate.update(SqlSessionTemplate.java:294)
	at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:62)
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
	at com.sun.proxy.$Proxy102.updateByConditionSelective(Unknown Source)
	at com.xes.payplatform.ledger.common.service.impl.WechatServiceImpl.operateNotMatchWechat(WechatServiceImpl.java:205)
	at com.xes.payplatform.ledger.common.service.impl.WechatServiceImpl$$FastClassBySpringCGLIB$$5b9bb00.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
	at com.xes.payplatform.ledger.common.service.impl.WechatServiceImpl$$EnhancerBySpringCGLIB$$13d23fd.operateNotMatchWechat(<generated>)
	at com.xes.payplatform.ledger.common.service.impl.PlateformAndFinanceCheckingServiceImpl.doCheckedLedgerDatas(PlateformAndFinanceCheckingServiceImpl.java:555)
	at com.xes.payplatform.ledger.common.service.impl.PlateformAndFinanceCheckingServiceImpl.financeChecking(PlateformAndFinanceCheckingServiceImpl.java:529)
	at com.xes.payplatform.ledger.common.service.impl.PlateformAndFinanceCheckingServiceImpl$$FastClassBySpringCGLIB$$898ef38d.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
	at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
	at com.xes.payplatform.ledger.common.service.impl.PlateformAndFinanceCheckingServiceImpl$$EnhancerBySpringCGLIB$$a4273cf4.financeChecking(<generated>)
	at com.xes.payplatform.ledger.task.service.FinanceCheckTaskHandler.execute(FinanceCheckTaskHandler.java:36)
	at com.dangdang.ddframe.job.executor.type.SimpleJobExecutor.process(SimpleJobExecutor.java:41)
	at com.dangdang.ddframe.job.executor.AbstractElasticJobExecutor.process(AbstractElasticJobExecutor.java:206)
	at com.dangdang.ddframe.job.executor.AbstractElasticJobExecutor.access$000(AbstractElasticJobExecutor.java:47)
	at com.dangdang.ddframe.job.executor.AbstractElasticJobExecutor$1.run(AbstractElasticJobExecutor.java:185)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction

开启了 ?之旅,经过各种查询发现是由于where后面的字段没有加索引,导致更新时锁表,我去,竟然是这么回事,于是一顿神搜索,去刨根问底一下具体原因。
MySQL基本上有2种搜索引擎,一、MyISAM:支持表级锁;二、InnoDB:支持表级锁和行级锁,但是默认支持的是行级锁。MySQL支持的锁主要有3种类型,表级锁、行级锁、页面锁。区别如下,
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

行级锁并不是直接锁记录,而是锁索引,如果一条SQL语句用到了主键索引,mysql会锁住主键索引;如果一条语句操作了非主键索引,mysql会先锁住非主键索引,再锁定主键索引。

接下来直接在mysql上验证下该问题
1.查询当前sql是否支持自动提交
select @@autocommit;
结果如下:
在这里插入图片描述
现在查询结果为1,表明是自动提交,需要通过下面语句设置为非自动提交
set autocommit = 0
然后创建新的数据表去验证以上问题,建表sql

 CREATE TABLE tb_user (
  id int(20) NOT NULL AUTO_INCREMENT,
  name varchar(32) DEFAULT NULL,
  phone varchar(11) DEFAULT NULL,
  create_id varchar(32) DEFAULT NULL,
  create_time datetime DEFAULT CURRENT_TIMESTAMP,
  update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  status int(2) NOT NULL,
  PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

1.无索引情况下更新数据
begin;-- 开启事务
update tb_user set phone=‘15167891234’ where name=‘小花’;-- 修改,先别commit事务
再开一个窗口,直接运行命令:
update tb_user set phone=‘15167891234’ where name=“小明”;-- 发现一直卡着不能执行
但将第一个窗口的sql COMMIT之后,第二个窗口的更新语句就能执行了,说明在where条件后没索引的情况下锁表
2.有索引情况下更新数据
create index index_name on tb_user(name);
加完索引之后继续按照1的步骤去执行,发现窗口2不会卡着了,立马执行了,说明没有锁表了,然后将相同的update语句在打开的2个窗口内执行,发现第2个窗口会一直卡着,说明在where条件后有索引的情况下锁行

总结

在update/delete情况下,如果没有索引,会锁表,如果加了索引,就会锁行。但是其中过滤条件最好在主键索引情况下执行,因为过滤条件在非主键索引情况下,mysql会先锁住非主键索引,再锁定主键索引,如果此时恰好该行记录又根据主见索引更新,有可能也会发生冲突。
ps:数据库锁表时间一般为50s。
– 查看那些表锁到了
show OPEN TABLES where In_use > 0;
– 查看进程号
show processlist;
–删除进程
kill 1085850;
在这里插入图片描述
参数
id #ID标识,要kill一个语句的时候很有用
use #当前连接用户
host #显示这个连接从哪个ip的哪个端口上发出
db #数据库名
command #连接状态,一般是休眠(sleep),查询(query),连接(connect)
time #连接持续时间,单位是秒
state #显示当前sql语句的状态
info #显示这个sql语句

其中state详解:https://blog.csdn.net/sunqingzhong44/article/details/70570728

猜你喜欢

转载自blog.csdn.net/qq_30035133/article/details/86704341