1. 問(wèn)題: SQL中數(shù)值類型數(shù)據(jù)和字符串比較蛾娶,會(huì)優(yōu)先取字符串中包含的數(shù)值猫牡,如果不包含數(shù)值則默認(rèn)為0
2. 問(wèn)題:重復(fù)Bean注入
org.springframework.beans.factory.NoUniqueBeanDefinitionException
解決方法:
1. @Qualifier("beanName")
在引用注入時(shí),使用@Qualifier注解來(lái)指定注入
2. @Primary
在定義新的Bean時(shí)旧找,用于聲明優(yōu)先注入
-
3. 問(wèn)題: SQL中有
having
函數(shù)作為查詢條件回俐,使用PageHelper插件報(bào)錯(cuò)Unknown column '' in 'having clause'
原因:因?yàn)?strong>PageHelper插件會(huì)默認(rèn)執(zhí)行一條count(0)
統(tǒng)計(jì)語(yǔ)句,此時(shí)會(huì)把原分頁(yè)查詢SQL中的查詢結(jié)果字段替換為count(0)
函數(shù)诱建,此時(shí)having
函數(shù)用到的字段邊會(huì)不存在導(dǎo)致SQL執(zhí)行報(bào)錯(cuò)
解決方法:
1. SQL語(yǔ)句中加一個(gè)DISTINT函數(shù)蝴蜓,這樣PageHelper插件執(zhí)行統(tǒng)計(jì)函數(shù)時(shí)便不會(huì)替換查詢字段,而把之前的查詢語(yǔ)句當(dāng)作一個(gè)子表去重新包裹指定統(tǒng)計(jì)函數(shù)
2. 手動(dòng)修改原SQL語(yǔ)句為嵌套的子表查詢
-
4. 問(wèn)題: 在數(shù)據(jù)庫(kù)中執(zhí)行
SELECT * FROM information_schema.innodb_trx;
語(yǔ)句查詢?cè)趫?zhí)行中的事務(wù)時(shí)俺猿,出現(xiàn)LOCK WAIT(mysql死鎖)
茎匠。
注: 超過(guò)數(shù)據(jù)庫(kù)配置的鎖等待時(shí)間:innodb_lock_wait_timeout
則會(huì)拋出異常LOCK_WAIT_TIMEOUT
。
原因一: 高并發(fā)的情況下押袍,Spring事務(wù)造成數(shù)據(jù)庫(kù)死鎖诵冒,后續(xù)操作超時(shí)拋出異常LOCK_WAIT_TIMEOUT
。
原因二: MySQL數(shù)據(jù)庫(kù)隔離級(jí)別為可重復(fù)讀(REPEATABLE READ
)時(shí)谊惭,Spring的聲明式事務(wù)@Transactional
的事務(wù)傳播中存在嵌套事務(wù)(傳播行為如:PROPAGATION_REQUIRES_NEW
/PROPAGATION_NESTED
)汽馋,主事務(wù)對(duì)A表產(chǎn)生了間隙鎖(Gap Lock)
,嵌套事務(wù)又對(duì)此表進(jìn)行了更新操作(如:insert/update/delete)
正好在在間隙鎖加鎖范圍內(nèi)圈盔,則會(huì)進(jìn)入鎖等待LOCK WAIT(mysql死鎖)
豹芯,超過(guò)數(shù)據(jù)庫(kù)配置的鎖等待時(shí)間:innodb_lock_wait_timeout
則會(huì)拋出異常LOCK_WAIT_TIMEOUT
。
注:間隙鎖(Gap Lock)
是Innodb在可重復(fù)讀(REPEATABLE READ)
提交下為了解決幻讀問(wèn)題時(shí)引入的鎖機(jī)制驱敲。
解決方法:
# 應(yīng)急方法:
1. show full processlist; 找出出現(xiàn)問(wèn)題的進(jìn)程铁蹈;
2. kill掉出現(xiàn)問(wèn)題的進(jìn)程。
# 根本解決辦法:
1. select * from information_schema.innodb_trx; 查看有是哪些事務(wù)占據(jù)了表資源
2. 找到對(duì)應(yīng)的程序代碼众眨,如原因二是嵌套事務(wù)導(dǎo)致握牧,可以把主事務(wù)中因?yàn)槲疵兴饕龑?dǎo)致的鎖表操作進(jìn)行修改容诬,避免鎖表
# 其他辦法:
增加鎖等待時(shí)間,即增大下面配置項(xiàng)參數(shù)值沿腰,單位為秒(s) innodb_lock_wait_timeout=500
優(yōu)化存儲(chǔ)過(guò)程览徒,事務(wù)避免過(guò)長(zhǎng)時(shí)間的等待
間隙鎖相關(guān)可參考以下兩篇文章:
間隙鎖詳解
MySQL的鎖機(jī)制 - 記錄鎖、間隙鎖矫俺、臨鍵鎖
-
5. 問(wèn)題: 處理第三方回調(diào)時(shí)吱殉,需要加鎖(如:分布式鎖
redisson
)來(lái)保證只處理一次回調(diào)請(qǐng)求(如:易寶支付回調(diào)會(huì)反復(fù)發(fā)送回調(diào)確認(rèn)),如果加鎖和釋放鎖的操作是在事務(wù)內(nèi)部厘托,高并發(fā)情況下會(huì)因?yàn)?strong>鎖釋放了但是事務(wù)沒(méi)提交導(dǎo)致重復(fù)執(zhí)行鎖內(nèi)的代碼片段進(jìn)而影響到數(shù)據(jù)的準(zhǔn)確性
解決方法:
# 方法一:
在加鎖執(zhí)行的代碼中友雳,新增個(gè)處理標(biāo)記,聲明此段代碼已被處理過(guò)铅匹,無(wú)需重復(fù)處理
如:在redis中設(shè)置個(gè)有期限的唯一標(biāo)識(shí)(有效期根據(jù)實(shí)際運(yùn)行場(chǎng)景規(guī)定)
# 方法二:
可以在存信息的數(shù)據(jù)庫(kù)表中新增唯一索引來(lái)確保數(shù)據(jù)的唯一性
推薦兩種方法一押赊,方法二結(jié)合使用
# 方法三:
把鎖內(nèi)的代碼片段抽取出來(lái)單獨(dú)聲明事務(wù),確保鎖釋放是在事務(wù)提交后進(jìn)行的
-
6. 問(wèn)題(間隙鎖—場(chǎng)景描述): 秒殺提交訂單為了代碼執(zhí)行效率包斑,把創(chuàng)建易寶訂單(對(duì)接的第三方支付)的步驟放到了生成本地訂單步驟之前流礁。創(chuàng)建易寶訂單的代碼塊是復(fù)用的之前普通下單的,所以有對(duì)訂單的修改操作(用于保存易寶流水號(hào))罗丰,但因?yàn)榇藭r(shí)本地訂單在數(shù)據(jù)庫(kù)中還未生成神帅,所以根據(jù)主鍵的修改操作會(huì)產(chǎn)生間隙鎖。因?yàn)槊霘?chǎng)景又會(huì)有多個(gè)線程同時(shí)在運(yùn)行萌抵,多個(gè)線程同時(shí)執(zhí)行這段代碼塊找御,會(huì)因?yàn)?strong>間隙鎖導(dǎo)致死鎖,這時(shí)在鎖未釋放的時(shí)候绍填,再對(duì)訂單表進(jìn)行插入操作霎桅,則會(huì)直接報(bào)錯(cuò)(
Deadlock found when trying to get lock
)
注: 數(shù)據(jù)庫(kù)隔離級(jí)別為可重復(fù)讀(REPEATABLE READ
)
解決辦法: 這里是對(duì)“創(chuàng)建易寶訂單”代碼塊里的保存易寶交易單號(hào)信息這段代碼進(jìn)行了非空判斷,對(duì)本地?cái)?shù)據(jù)庫(kù)中不存在的訂單不保存相關(guān)易寶流水號(hào)(可以在易寶后臺(tái)根據(jù)交易訂單號(hào)獲取相關(guān)信息)
# 報(bào)錯(cuò)信息
### Error updating database. Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
-
7. 問(wèn)題: sql語(yǔ)句中有空值的列使用
in
或者not in
操作會(huì)失效
解決辦法:
1. 避免出現(xiàn)空值列(賦予默認(rèn)值)
2. 用 `or` 連接一個(gè)為空的條件 `is null`
例如:column_1 is null or column_1 not in (1,2)
-
8. 問(wèn)題: Druid連接池配置開(kāi)啟
removeAbandoned: true
(強(qiáng)制關(guān)閉連接時(shí)長(zhǎng)大于removeAbandonedTimeoutMillis的數(shù)據(jù)庫(kù)連接)讨永,并且removeAbandonedTimeoutMillis
(連接最大生命周期)設(shè)置的時(shí)間過(guò)短滔驶,導(dǎo)致數(shù)據(jù)庫(kù)連接被強(qiáng)制關(guān)閉。
報(bào)錯(cuò)信息:
[ERROR] - com.alibaba.druid.pool.DruidDataSource.removeAbandoned(DruidDataSource.java:2979) - abandon connection, owner thread: xxx, connected at : xxx, open stackTrace
解決辦法:
1. 將removeAbandoned這個(gè)配置設(shè)置為false或者不設(shè)置(默認(rèn)就是false)
或者
2. 將removeAbandonedTimeoutMillis這個(gè)時(shí)間配置調(diào)大
# 時(shí)間設(shè)置為30分鐘(單位:秒)
remove-abandoned-timeout: 1800
# 時(shí)間設(shè)置為30分鐘(單位:毫秒)
remove-abandoned-timeout-millis: 1800000
配置 | 默認(rèn)值 | 說(shuō)明 |
---|---|---|
removeAbandoned | false | 是否強(qiáng)制關(guān)閉連接時(shí)長(zhǎng)大于removeAbandonedTimeoutMillis的連接 |
removeAbandonedTimeoutMillis | 300 * 1000 (單位:毫秒) | 一個(gè)連接從被連接到被關(guān)閉之間的最大生命周期 |
logAbandoned | false | 強(qiáng)制關(guān)閉連接時(shí)是否記錄日志 |
- 9. 問(wèn)題:三元表達(dá)式空指針問(wèn)題卿闹,包裝類型自動(dòng)拆箱揭糕,空值null拆箱操作,也就是Integer的intValue()方法報(bào)空指針锻霎。
DTO dto = new DTO();
System.out.println(build != null ? build.getProperty() : 1);
解決辦法:
DTO dto = new DTO();
System.out.println(build != null ? build.getProperty() : Integer.valueOf(1));
-
10. 問(wèn)題:Spring事務(wù)默認(rèn)的傳播行為:Propagation.REQUIRED插佛。在使用Spring事務(wù)時(shí),在一個(gè)事務(wù)A中又開(kāi)了一個(gè)事務(wù)B(即存在嵌套事務(wù))量窘,當(dāng)事務(wù)B發(fā)生異常時(shí),在A中將異常catch后事務(wù)B會(huì)進(jìn)行回滾操作氢拥,此時(shí)異常被catch掉蚌铜,事務(wù)A依然會(huì)拋出如上異常并進(jìn)行回滾操作锨侯。
原因一:因?yàn)閙ethodB的傳播屬性設(shè)置為PROPAGATION_REQUIRED,PROPAGATION_REQUIRED的意思是冬殃,當(dāng)前有事務(wù)囚痴,則使用當(dāng)前事務(wù),當(dāng)前無(wú)事務(wù)則創(chuàng)建事務(wù)审葬。由于methodA的傳播屬性也為PROPAGATION_REQUIRED深滚,所以methodA會(huì)創(chuàng)建一個(gè)事務(wù),然后methodB與methodA使用同一個(gè)事務(wù)涣觉,methodB出現(xiàn)異常后痴荐,將當(dāng)前事務(wù)標(biāo)志位回滾,由于在methodA中做了trycatch處理官册,程序沒(méi)有終止而是繼續(xù)往下走生兆,當(dāng)事務(wù)commit時(shí),check狀態(tài)膝宁,發(fā)現(xiàn)鸦难,需要事務(wù)回滾,所以才會(huì)出現(xiàn)不可預(yù)知的事務(wù)異常:因?yàn)槭聞?wù)被標(biāo)志位回滾员淫,所以事務(wù)回滾合蔽。
也就是說(shuō):methodA與methodB共用一個(gè)事務(wù),methodB將事務(wù)標(biāo)志為回滾介返,methodA中commit這個(gè)事務(wù)拴事,然后,出現(xiàn)事務(wù)已經(jīng)被標(biāo)志回滾(methodB標(biāo)志的)的異常信息映皆。
解決辦法:
1. methodA與methodB在邏輯上不應(yīng)該屬于同一個(gè)事務(wù)挤聘,那么將methodB的事務(wù)傳播屬性修改為PROPAGATION_REQUIRES_NEW,這樣捅彻,執(zhí)行methodB時(shí)组去,會(huì)創(chuàng)建一個(gè)新的事務(wù),不影響methodA中的事務(wù)步淹。
2. 業(yè)務(wù)A與業(yè)務(wù)B在業(yè)務(wù)邏輯上就應(yīng)該屬于同一個(gè)事務(wù)从隆,但是methodB的失敗與否不能影響methodA的事務(wù)提交,那么仍然在methodA中try catch methodB,并將methodB設(shè)置為PROPAGATION_NESTED缭裆,它的意思是键闺,methodB是一個(gè)子事務(wù),有一個(gè)savepoint澈驼,失敗時(shí)會(huì)回滾到savepoint辛燥,不影響methodA,如果成功則A、B一起提交挎塌,A與B都是一個(gè)事務(wù)徘六,只是B是一個(gè)子事務(wù)。
Spring事務(wù)報(bào)錯(cuò): org.springframework.transaction.UnexpectedRollbackException