踩坑1:數據庫事務超時
先了解一下“鎖互斥機制”
比如客戶端1拿到鎖之后,還未執行完代碼,此時客戶端2來嘗試拿鎖,
第一個判斷:客戶端2這時候會發現這個鎖已經存在了
第二個判斷:是否是客戶端2加的鎖,但是也發現不是,因為是客戶端1加的鎖
然后客戶端2會獲取到這個鎖的剩余生存時間,此時客戶端2會進入一個while循環,不停的嘗試加鎖。
偽代碼:
@Transaction
???public void lock() {
????????while (true) {
????????????boolean flag = this.getLock(key);
????????????if (flag) {
????????????????insert();
????????????}
????????}
????}
這里有個@Transaction事務注解,比如
boolean flag = this.getLock(key);
這塊代碼遲遲拿不到鎖,超過了事務的時間限制,程序就會報數據庫事務超時的異常;或者我們在執行insert();這塊代碼時,執行時間太長可能也會超過事務的時間限制,從而也導致程序報數據庫超時的異常。一般解決這種問題就要:將數據庫事務改為手動提交、回滾事務。
@Autowired
????DataSourceTransactionManager dataSourceTransactionManager;
????@Transaction
????public void lock() {
//手動開啟事務
????????TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
????????try {
????????????while (true) {
????????????????boolean flag = this.getLock(key);
????????????????if (flag) {
????????????????????insert();
//手動提交事務
????????????????????dataSourceTransactionManager.commit(transactionStatus);
????????????????}
????????????}
????????} catch (Exception e) {
//手動回滾事務
????????????dataSourceTransactionManager.rollback(transactionStatus);
????????}
????}
踩坑2:redis的鎖未被釋放,連接池爆滿
這種情況是一種低級錯誤,由于當前線程獲取到redis鎖,處理完業務后未及時釋放鎖,導致其它線程會一直嘗試獲取鎖阻塞,例如:用Jedis客戶端會報如下的錯誤信息
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
redis線程池已經沒有空閑線程來處理客戶端命令。
解決辦法
void update(){
????do{
????????redis=JedisUtil.getJedis();
????????flag = getLock(key,redis);
????????if(flag){
????????????update();
????????}else{
//釋放當前redis連接
//由于我們的業務場景屬于比較耗時的業務型,所以在這里休眠1000毫秒
????????????redis.close();
????????????sleep(1000);
????????}
????}while(true)
}
1.當前請求獲取鎖,如果獲取不到,則釋放當前連接,并休眠一會;
2.合理配置redis連接池大小,主要參考具體業務場景的并發量來設置;
3.如果是重入鎖未拿到鎖后,線程可以釋放當前連接并且sleep一段時間。