日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql乐观锁重试_乐观锁加重试,并发更新数据库一条记录导致:Lock wait timeout exceeded...

發布時間:2024/9/27 数据库 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql乐观锁重试_乐观锁加重试,并发更新数据库一条记录导致:Lock wait timeout exceeded... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

背景:

mysql數據庫,用戶余額表有一個version(版本號)字段,作為樂觀鎖。

更新方法有事務控制:

@Transactional(rollbackFor = Exception.class)

更新時,比對版本號,如果版本號不一致,則更新失敗。

有重試機制,如果更新失敗,則查詢最新版本號,再次更新,重試超過5次,報錯退出。

更新的核心方法:

public boolean updateUserAccount(Long userId, int amount) {

boolean retryable;

int attemptNumber = 0;

do {

// 查詢最新版本號

UserAccount userAccount = accountMapper.selectByPrimaryKey(userId);

long oldVersion = userAccount.getVersion();

// 更新

boolean success = accountMapper.updateBalance(amount, new Date(), userId, oldVersion) > 0;

if (success) {

return true;

} else {

attemptNumber++;

retryable = attemptNumber < 5;

if (attemptNumber == 5) {

log.error("超過最大重試次數");

break;

}

try {

Thread.sleep(300);

} catch (InterruptedException e) {

log.error(e);

}

}

} while (retryable);

return false;

}

更新語句:

UPDATE user_account

SET

balance = balance - #{amount,jdbcType=INTEGER},

update_time = #{updateTime,jdbcType=TIMESTAMP},

version = #{version,jdbcType=BIGINT} + 1

WHERE balance > #{amount,jdbcType=INTEGER}

AND user_id = #{userId,jdbcType=BIGINT}

AND version = #{version,jdbcType=BIGINT};

在并發更新時,報異常:Lock wait timeout exceeded

分析:

根據日志分析出:

線程a、b幾乎同時到達

線程a查詢版本號:856

線程a更新數據庫:成功

數據庫當前版本號:857

線程b查詢到的版本號:856(實際已不是最新)

線程b更新數據庫:失敗

線程b重試,查詢版本號:856

線程b更新數據庫:失敗

。。。

線程b超過重試次數,退出

線程b重試的過程中,又有其他線程到來,比如c,d,e

線程c查詢版本號:857

線程c更新數據庫:阻塞,因為b拿到鎖一直在重試

線程d查詢版本號:857

線程d更新數據庫:阻塞,因為b拿到鎖一直在重試

線程b超次數退出后,c,d,e爭搶鎖

d拿到鎖,更新數據庫:成功

數據庫當前版本號:858

線程c查詢到的版本號:857(實際已不是最新)

線程c更新數據庫:失敗

線程c重試,查詢版本號:857

線程c更新數據庫:失敗

。。。

線程c超過重試次數,退出

重試次數過多,事務執行時間超過mysql默認的鎖等待時間(50s),就會報出:Lock wait timeout exceeded

為什么線程讀不到最新的版本號呢?原來是用到了事務,且mysql默認事務隔離級別Repeatable?Read,把隔離級別改為READ_COMMITTED,問題解決:

@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)

分析了這么多,解決問題其實只需要一行代碼。

總結

以上是生活随笔為你收集整理的mysql乐观锁重试_乐观锁加重试,并发更新数据库一条记录导致:Lock wait timeout exceeded...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。