厦门网站制作哪里好薇,企业所得税规避50种,湛蓝 网站开发,追求无我记一次mysql事务并发优化
背景 事情的情况大致是这样的。一个扣减库存的业务上线以后#xff0c;隔几天会报一次错#xff0c;错误内容如下#xff1a; ERROR - exception: UncategorizedSQLException,detail:org.springframework.jdbc.UncategorizedSQ…记一次mysql事务并发优化
背景 事情的情况大致是这样的。一个扣减库存的业务上线以后隔几天会报一次错错误内容如下 ERROR - exception: UncategorizedSQLException,detail:org.springframework.jdbc.UncategorizedSQLException:
### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLQueryInterruptedException: Query execution was interrupted
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: UPDATE a SET stock stock - ? WHERE id ? and stock ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLQueryInterruptedException: Query execution was interrupted
; uncategorized SQLException for SQL []; SQL state [70100]; error code [1317]; Query execution was interrupted; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLQueryInterruptedException: Query execution was interrupted
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
at com.sun.proxy.$Proxy64.update(null:-1)
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)这块业务之前一直都是正常的迁移以后做的唯一大的改动就是加了java事务注解所以怀疑是否和事务有关。
排查过程 首先现寻找报错的关键信息第一个看到的是UncategorizedSQLException。查看这个类的源代码说明如下 /*** Exception thrown when we cant classify a SQLException into* one of our generic data access exceptions.** author Rod Johnson* author Juergen Hoeller*/可以看到这个类是Spring无法归类的一个SQL异常所以从这个异常我们是看不出什么内容的继续往下。 MySQLQueryInterruptedException: Query execution was interrupted 发现这个执行是超时被kill了。一般来讲我们公司dba设置的mysql超时时间是500ms。是否是因为数据量太大没有走到索引才导致update操作执行时间太长被kill了呢看一下update语句里 UPDATE a SET stock stock - ? WHERE id ? and stock ? 马上排除了这个可能性。因为where条件里有id查询必定会走主键索引不可能没有走到索引。那会是什么原因呢 上面我们提到这个业务操作里是开启了事务的还原一下大体的执行情况。 start transaction;
UPDATE a SET stock stock - 1 WHERE id 100 and stock 1;INSERT INTO a (num) values (1);
commit;查了一下当时的日志发现1秒内有大约200条请求对一条记录做更新库存的操作。线索渐渐清晰起来了事故现场大致应该是这样的
结论
由于开启了事务在高并发地对一条记录进行更新的情况下多个请求会进入排队系统。由于锁的竞争是不公平的当多个事务同时对一条记录进行更新时极端情况下就可能会出现一个更新操作进去排队系统以后一直拿不到锁超过500ms被kill了。
细节分析
以上的业务操作update会先申请行锁拿到行锁以后进行更新更新完以后会执行插入操作。那么在插入操作的时候是否需要申请锁呢答案是肯定的不过这里的插入操作使用的是自增锁。那自增锁是什么级别的锁呢如果存在自增字段MySQL会维护一个自增锁和自增锁相关的一个参数为5.1.22版本之后加入innodb_autoinc_lock_mode可以设定3个值0120traditonal 每次都会产生表锁1consecutive 会产生一个轻量锁simple insert会获得批量的锁保证连续插入2interleaved 不会锁表来一个处理一个并发最高Myisam引擎均为traditionalInnoDB默认为1轻量锁。所以在InnoDB的情况下这里的insert操作的性能比update操作更高。
优化 有了以上结论以后那如何优化呢 最简单的方案就是减少持有锁的时间处理方式非常简单将更新操作放到最后执行从而缩短更新锁的持有时间避免类似的超时问题。 start transaction;INSERT INTO a (num) values (1);
UPDATE a SET stock stock - 1 WHERE id 100 and stock 1;
commit;-----------------------------------------------------------------------------------
offer突击训练营简介
1针对不知道怎么面试面试没有信心的小伙伴我们会给你一个offer保障。
2我们会监督你15-20天内把面试体系技术点掌握至少7成这样足够你去找到满意的工作了。
3我们是面向面试学习指导不会带你们去写代码会把项目真实开发的迭代过程和技术细节如何实现业务功能都详细教清楚你能在面试中流畅表达清楚就行了项目经验你不用担心(技术老师提供的真实项目经验肯定拿的出手)自己学和别人带着系统学效率完全不一样。
详情请点击这里offer突击训练营给你一个offer的保障求职跳槽的看过来