最近查看后台日志,发现出现了大批量的锁表日志,顿时产生好多不解,报错日志如下:
### 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()
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()
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()
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()
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