-
1. 问题: SQL中数值类型数据和字符串比较,会优先取字符串中包含的数值,如果不包含数值则默认为0
-
2. 问题:重复Bean注入
org.springframework.beans.factory.NoUniqueBeanDefinitionException
解决方法:
1. @Qualifier("beanName")
在引用注入时,使用@Qualifier注解来指定注入
2. @Primary
在定义新的Bean时,用于声明优先注入
- 3. 问题: SQL中有
having
函数作为查询条件,使用PageHelper插件报错Unknown column '' in 'having clause'
原因:因为PageHelper插件会默认执行一条count(0)
统计语句,此时会把原分页查询SQL中的查询结果字段替换为count(0)
函数,此时having
函数用到的字段边会不存在导致SQL执行报错
解决方法:
1. SQL语句中加一个DISTINT函数,这样PageHelper插件执行统计函数时便不会替换查询字段,而把之前的查询语句当作一个子表去重新包裹指定统计函数
2. 手动修改原SQL语句为嵌套的子表查询
- 4. 问题: 在数据库中执行
SELECT * FROM information_schema.innodb_trx;
语句查询在执行中的事务时,出现LOCK WAIT(mysql死锁)
。
注: 超过数据库配置的锁等待时间:innodb_lock_wait_timeout
则会抛出异常LOCK_WAIT_TIMEOUT
。
原因一: 高并发的情况下,Spring事务造成数据库死锁,后续操作超时抛出异常LOCK_WAIT_TIMEOUT
。
原因二: MySQL数据库隔离级别为可重复读(REPEATABLE READ
)时,Spring的声明式事务@Transactional
的事务传播中存在嵌套事务(传播行为如:PROPAGATION_REQUIRES_NEW
/PROPAGATION_NESTED
),主事务对A表产生了间隙锁(Gap Lock)
,嵌套事务又对此表进行了更新操作(如:insert/update/delete)
正好在在间隙锁加锁范围内,则会进入锁等待LOCK WAIT(mysql死锁)
,超过数据库配置的锁等待时间:innodb_lock_wait_timeout
则会抛出异常LOCK_WAIT_TIMEOUT
。
注:间隙锁(Gap Lock)
是Innodb在可重复读(REPEATABLE READ)
提交下为了解决幻读问题时引入的锁机制。
解决方法:
# 应急方法:
1. show full processlist; 找出出现问题的进程;
2. kill掉出现问题的进程。
# 根本解决办法:
1. select * from information_schema.innodb_trx; 查看有是哪些事务占据了表资源
2. 找到对应的程序代码,如原因二是嵌套事务导致,可以把主事务中因为未命中索引导致的锁表操作进行修改,避免锁表
# 其他办法:
增加锁等待时间,即增大下面配置项参数值,单位为秒(s) innodb_lock_wait_timeout=500
优化存储过程,事务避免过长时间的等待
间隙锁相关可参考以下两篇文章:
间隙锁详解
MySQL的锁机制 - 记录锁、间隙锁、临键锁
- 5. 问题: 处理第三方回调时,需要加锁(如:分布式锁
redisson
)来保证只处理一次回调请求(如:易宝支付回调会反复发送回调确认),如果加锁和释放锁的操作是在事务内部,高并发情况下会因为锁释放了但是事务没提交导致重复执行锁内的代码片段进而影响到数据的准确性
解决方法:
# 方法一:
在加锁执行的代码中,新增个处理标记,声明此段代码已被处理过,无需重复处理
如:在redis中设置个有期限的唯一标识(有效期根据实际运行场景规定)
# 方法二:
可以在存信息的数据库表中新增唯一索引来确保数据的唯一性
推荐两种方法一,方法二结合使用
# 方法三:
把锁内的代码片段抽取出来单独声明事务,确保锁释放是在事务提交后进行的
- 6. 问题(间隙锁—场景描述): 秒杀提交订单为了代码执行效率,把创建易宝订单(对接的第三方支付)的步骤放到了生成本地订单步骤之前。创建易宝订单的代码块是复用的之前普通下单的,所以有对订单的修改操作(用于保存易宝流水号),但因为此时本地订单在数据库中还未生成,所以根据主键的修改操作会产生间隙锁。因为秒杀场景又会有多个线程同时在运行,多个线程同时执行这段代码块,会因为间隙锁导致死锁,这时在锁未释放的时候,再对订单表进行插入操作,则会直接报错(
Deadlock found when trying to get lock
)
注: 数据库隔离级别为可重复读(REPEATABLE READ
)
解决办法: 这里是对“创建易宝订单”代码块里的保存易宝交易单号信息这段代码进行了非空判断,对本地数据库中不存在的订单不保存相关易宝流水号(可以在易宝后台根据交易订单号获取相关信息)
# 报错信息
### Error updating database. Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
- 7. 问题: sql语句中有空值的列使用
in
或者not in
操作会失效
解决办法:
1. 避免出现空值列(赋予默认值)
2. 用 `or` 连接一个为空的条件 `is null`
例如:column_1 is null or column_1 not in (1,2)
- 8. 问题: Druid连接池配置开启
removeAbandoned: true
(强制关闭连接时长大于removeAbandonedTimeoutMillis的数据库连接),并且removeAbandonedTimeoutMillis
(连接最大生命周期)设置的时间过短,导致数据库连接被强制关闭。
报错信息:
[ERROR] - com.alibaba.druid.pool.DruidDataSource.removeAbandoned(DruidDataSource.java:2979) - abandon connection, owner thread: xxx, connected at : xxx, open stackTrace
解决办法:
1. 将removeAbandoned这个配置设置为false或者不设置(默认就是false)
或者
2. 将removeAbandonedTimeoutMillis这个时间配置调大
# 时间设置为30分钟(单位:秒)
remove-abandoned-timeout: 1800
# 时间设置为30分钟(单位:毫秒)
remove-abandoned-timeout-millis: 1800000
配置 | 默认值 | 说明 |
---|---|---|
removeAbandoned | false | 是否强制关闭连接时长大于removeAbandonedTimeoutMillis的连接 |
removeAbandonedTimeoutMillis | 300 * 1000 (单位:毫秒) | 一个连接从被连接到被关闭之间的最大生命周期 |
logAbandoned | false | 强制关闭连接时是否记录日志 |