踩坑1:數(shù)據(jù)庫(kù)事務(wù)超時(shí)
先了解一下“鎖互斥機(jī)制”
比如客戶(hù)端1拿到鎖之后厉膀,還未執(zhí)行完代碼类浪,此時(shí)客戶(hù)端2來(lái)嘗試拿鎖,
第一個(gè)判斷:客戶(hù)端2這時(shí)候會(huì)發(fā)現(xiàn)這個(gè)鎖已經(jīng)存在了
第二個(gè)判斷:是否是客戶(hù)端2加的鎖纬乍,但是也發(fā)現(xiàn)不是跋选,因?yàn)槭强蛻?hù)端1加的鎖
然后客戶(hù)端2會(huì)獲取到這個(gè)鎖的剩余生存時(shí)間,此時(shí)客戶(hù)端2會(huì)進(jìn)入一個(gè)while循環(huán)官脓,不停的嘗試加鎖线定。
偽代碼:
@Transaction
???public void lock() {
????????while (true) {
????????????boolean flag = this.getLock(key);
????????????if (flag) {
????????????????insert();
????????????}
????????}
????}
這里有個(gè)@Transaction事務(wù)注解,比如
boolean flag = this.getLock(key);
這塊代碼遲遲拿不到鎖确买,超過(guò)了事務(wù)的時(shí)間限制斤讥,程序就會(huì)報(bào)數(shù)據(jù)庫(kù)事務(wù)超時(shí)的異常;或者我們?cè)趫?zhí)行insert();這塊代碼時(shí)湾趾,執(zhí)行時(shí)間太長(zhǎng)可能也會(huì)超過(guò)事務(wù)的時(shí)間限制芭商,從而也導(dǎo)致程序報(bào)數(shù)據(jù)庫(kù)超時(shí)的異常。一般解決這種問(wèn)題就要:將數(shù)據(jù)庫(kù)事務(wù)改為手動(dòng)提交搀缠、回滾事務(wù)铛楣。
@Autowired
????DataSourceTransactionManager dataSourceTransactionManager;
????@Transaction
????public void lock() {
//手動(dòng)開(kāi)啟事務(wù)
????????TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
????????try {
????????????while (true) {
????????????????boolean flag = this.getLock(key);
????????????????if (flag) {
????????????????????insert();
//手動(dòng)提交事務(wù)
????????????????????dataSourceTransactionManager.commit(transactionStatus);
????????????????}
????????????}
????????} catch (Exception e) {
//手動(dòng)回滾事務(wù)
????????????dataSourceTransactionManager.rollback(transactionStatus);
????????}
????}
踩坑2:redis的鎖未被釋放,連接池爆滿
這種情況是一種低級(jí)錯(cuò)誤艺普,由于當(dāng)前線程獲取到redis鎖簸州,處理完業(yè)務(wù)后未及時(shí)釋放鎖,導(dǎo)致其它線程會(huì)一直嘗試獲取鎖阻塞歧譬,例如:用Jedis客戶(hù)端會(huì)報(bào)如下的錯(cuò)誤信息
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis線程池已經(jīng)沒(méi)有空閑線程來(lái)處理客戶(hù)端命令岸浑。
解決辦法
void update(){
????do{
????????redis=JedisUtil.getJedis();
????????flag = getLock(key,redis);
????????if(flag){
????????????update();
????????}else{
//釋放當(dāng)前redis連接
//由于我們的業(yè)務(wù)場(chǎng)景屬于比較耗時(shí)的業(yè)務(wù)型,所以在這里休眠1000毫秒
????????????redis.close();
????????????sleep(1000);
????????}
????}while(true)
}
1.當(dāng)前請(qǐng)求獲取鎖瑰步,如果獲取不到矢洲,則釋放當(dāng)前連接,并休眠一會(huì)缩焦;
2.合理配置redis連接池大小读虏,主要參考具體業(yè)務(wù)場(chǎng)景的并發(fā)量來(lái)設(shè)置责静;
3.如果是重入鎖未拿到鎖后,線程可以釋放當(dāng)前連接并且sleep一段時(shí)間盖桥。