在上一篇文章中我的程式因為每個消費執行緒對應了兩個事物,所以相當於一個消費執行緒就要佔用兩個資料庫連線,但是資料庫連線池配置的最大執行緒用的預設值20,不夠,所以會報類似Could not open IDBC Connection for transaction。。。等錯誤,如下圖
然後,我把資料庫連線池調高一倍,就解決了問題,但是我這種解決方案其實是有問題的,問題如下:
1、在同一個執行緒中,應該先提交事務再開啟新的事務在一個執行緒中應該先提交事務,然後再開啟一個新的事務,這樣子才不會導致一個執行緒佔用兩個事務(兩個資料庫連線)。
有人可能會問,那我把資料庫連結上限擴大一倍不就解決了麼?
資料庫連結擴大一倍是可以我解決,但是如果每個人寫程式碼都是有巢狀事務,那麼誰都不能預計那個程式碼嵌套了多少事務,每個執行緒會佔用多少個數據庫連線,也就不好配置程式的相關資料庫連線引數,這種情況下很容易就會出現在開發環境由於併發量不高不會出現問題然後到生產環境訪問量一上來了就爆炸了,只能狂燒腦細胞的處理。
所以我們一定要保證在同一個執行緒中,只有一個數據庫連線,如果逼不得已有這種邏輯,那麼也儘量少用,並且資料庫連線池的上限要比執行緒數大。
2、非必要不要把無關的TCP連結操作納入事務管理中比如我上一個例子的程式,邏輯是這樣子的業務邏輯執行完後,需要向RocketMQ登記一條訊息,在訊息登記失敗後我會插入一條失敗記錄在資料庫中。
@Transaction()public void sendMsg(){ try{ sendRocketMsg(); }catch(Exception){ insertSQL(); }}
邏輯本身沒有毛病,但是我們不應該在方法上加上@Transaction()註解,這樣子每次傳送訊息都會先開啟事務,也就是會新建一個數據庫連結,但是理論上我們的登記訊息是跟資料庫沒有關係的,沒有必要登記訊息就去佔用資料庫連線,如果此時資料庫連線有問題,那麼訊息也就再也發不出去,所以最好的邏輯是隻有在丟擲異常要進行登記訊息的時候才需要去開啟事務佔用連線,虛擬碼如下
```public void sendMsg(){ try{ sendRocketMsg(); }catch(Exception){ @Transaction() insertSQL(); }}
3、有對資料庫多次修改操作的邏輯必須要加入同一個事務中怎麼說呢,我們有一個消費程式,然後我發現積壓了幾十萬條訊息,檢查發現service方法沒有加上@Transaction,真是人才啊,看下邏輯進行了多次資料庫修改操作,這種情況會導致一個執行緒會佔用多個數據庫連線,那麼資料庫連線配置就更加不夠了,所以解決辦法就是加上事務註解,都放在同一個事務裡就好了。
總結1、在同一個執行緒中,應該先提交事務再開啟新的事務2、非必要不要把無關的TCP連結操作納入事務管理中3、有對資料庫多次修改操作的邏輯必須要加入同一個事務中4、一定要注意程式資料庫連線池的最大連線數是否足夠5、資料庫連線池的最大值一定要大於等於執行緒池的最大值