美文网首页
多线程并发修改表,获取锁等待超时

多线程并发修改表,获取锁等待超时

作者: zianL | 来源:发表于2020-05-17 11:00 被阅读0次

  @Test
    public void deadlock () throws Exception{

        OfferReceiveRequestDto dto = new OfferReceiveRequestDto();
        dto.setActivityId(527);
        dto.setOfferId("fa389470-5e33-48ce-ba05-7c78e62270db");
        dto.setGroupId("ad8ff217-4cb4-4279-bc40-6f19477d7b76");
        dto.setOfferType(OfferTypeEnum.point);
        int ou = 200000;

        for (int j = 0; j<ou;j++) {
            executor1.execute(new Runnable() {
                @Override
                public void run() {
                    ArrayList<OfferReceiveRequestDto.CustomNum> customNums = new ArrayList<>();
                    for (int i = 0; i < 5; i++) {
                        OfferReceiveRequestDto offerReceiveRequestDto = new OfferReceiveRequestDto();
                        OfferReceiveRequestDto.CustomNum customNum = offerReceiveRequestDto.new CustomNum();
                        customNum.setCardNum(i + "");
                        customNum.setIdNum(i + "");
                        customNums.add(customNum);
                    }
                    dto.setCustoms(customNums);
                    offerReceiveService.offerReceive(dto);
                }
            });
        }

        for (int j = 0; j<ou;j++) {
            executor2.execute(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 2; j++) {
                        List<String> ids = customPointService.getUnaccountedIds();
                    }
                }
            });
        }
        for (int j = 0; j<ou;j++) {
            executor3.execute(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 2; j++) {
                        //获取未入账集合
                        List<String> ids = customPointService.getUnaccountedIds();
                        ids.forEach(id -> {
                            TCustomPoint customPoint = customPointService.selectByKey(id);
                            offerReceiveService.updReceive(527, customPoint.getIdNum(), customPoint.getCreatedTime());
                        });
                    }
                }
            });
        }

        Thread.sleep(1000000L);
    }
    
    
    Exception in thread "pool-1-thread-191" org.springframework.dao.DeadlockLoserDataAccessException: 
### Error updating database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
### The error may involve com.gzcb.creditcard.coupon.mapper.TOfferReceiveMapper.insert-Inline
### The error occurred while setting parameters
### SQL: INSERT INTO t_offer_receive  ( serial_no,activity_id,group_id,id_num,offer_count,offer_receive_count,push_state,create_time ) VALUES( ?,?,?,?,?,?,?,? )
### Cause: com.mysql.jdbc.exceptions.jdbc4.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.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
    at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:266)
    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.$Proxy135.insert(Unknown Source)
    at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:278)
    at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:57)
    at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
    at com.sun.proxy.$Proxy174.insert(Unknown Source)
    at com.gzcb.creditcard.coupon.service.impl.OfferReceiveServiceImpl.inReceive(OfferReceiveServiceImpl.java:160)
    at com.gzcb.creditcard.coupon.service.impl.OfferReceiveServiceImpl.customReceiveOffer(OfferReceiveServiceImpl.java:138)
    at com.gzcb.creditcard.coupon.service.impl.OfferReceiveServiceImpl.lambda$offerReceive$0(OfferReceiveServiceImpl.java:86)
    at java.util.ArrayList.forEach(ArrayList.java:1249)
    at com.gzcb.creditcard.coupon.service.impl.OfferReceiveServiceImpl.offerReceive(OfferReceiveServiceImpl.java:76)
    at com.gzcb.creditcard.coupon.service.impl.OfferReceiveServiceImpl$$FastClassBySpringCGLIB$$728c7b8b.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
    at com.gzcb.creditcard.coupon.service.impl.OfferReceiveServiceImpl$$EnhancerBySpringCGLIB$$e3a5b5cd.offerReceive(<generated>)
    at com.gzcb.creditcard.coupon.IOfferReceiveServiceTest$1.run(IOfferReceiveServiceTest.java:94)
    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.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
    at sun.reflect.GeneratedConstructorAccessor124.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)
    at com.mysql.jdbc.Util.getInstance(Util.java:408)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:952)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3976)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3912)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2530)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2683)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2486)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1858)
    at com.mysql.jdbc.PreparedStatement.execute(PreparedStatement.java:1197)
    at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3051)
    at com.alibaba.druid.filter.FilterAdapter.preparedStatement_execute(FilterAdapter.java:1080)
    at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3049)
    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 sun.reflect.GeneratedMethodAccessor162.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke(PreparedStatementLogger.java:59)
    at com.sun.proxy.$Proxy213.execute(Unknown Source)
    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.GeneratedMethodAccessor200.invoke(Unknown Source)
    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.$Proxy210.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.GeneratedMethodAccessor217.invoke(Unknown Source)
    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)
    ... 23 more
Exception in thread "pool-1-thread-202" org.springframework.dao.DeadlockLoserDataAccessException: 
### Error updating database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting 

原因:多线程插入、查询、修改
在下列修改sql 中where条件没有用到索引就会扫全表,当进行批量修改时就容易导致获取锁等待超时,死锁等情况发生;

 <update id="updateReceiveCount">
    update
      t_offer_receive
    set
      offer_receive_count = offer_receive_count + #{num}
    where
      activity_id = #{activityId}
      and create_time = #{createTime}
      and id_num = #{idNum}
  </update>

解决方案 建立联合索引 对activity_id,id_numm,create_time 字段

ALTER table t_offer_receive add index agd(activity_id,id_num,create_time);

建立唯一索引更好
ALTER table t_offer_receive add UNIQUE index agd(activity_id,id_num,create_time);

再执行多线程代码,未发现死锁,以及等待锁超时等情况,业务处理时间也快了很多

相关文章

网友评论

      本文标题:多线程并发修改表,获取锁等待超时

      本文链接:https://www.haomeiwen.com/subject/naxuohtx.html