單體架構中事務的使用姿勢略談

yelangkingwuzuhu發表於2020-10-25

       不論是單體架構還是分散式架構,事務一定會使用到的。單體架構下,通常使用資料庫自帶的ACID機制加上Spring的AOP可以解決大多數場景的需求。分散式事務會比較複雜,有2PC,3PC,TCC等。本文暫不涉及分散式事務,考慮以後另寫文章,討論分散式事務。

       事務本身是用來保證一次請求或者操作的一致性,完整性的。因此不得不討論一下一次請求或者一次操作。一次請求可以理解為後臺一個controller方法的呼叫,單體架構下MVC模式使用較多。通常會分為三層,controller用於接收終端的請求,並將資料向serivce層進行傳遞,需要進行儲存的呼叫下層的dao(mapper)層或者cache層。

        那麼事務究竟放在哪一層比較合適呢?是controller還是service?或者dao?

從個人的工作經歷來看,把事務放在dao層的做法不是說沒有,只是比較少。通常情況下,一個dao的方法用於完成一個資料庫操作,刪除,修改或者新增。資料庫預設一條DDL就是一次事務,通過資料庫ACID機制就可以保證。

        其次,事務比較常見的是加在controller層,至於原因嘛?有的開發工程師認為既然一次請求就是一次事務,把事務加在controller似乎看起來也是沒毛病的。當然,這只是似乎而已。

        咋一聽起來,有一絲道理。仔細尋思,一次請求只是說終端對後臺呼叫一次,並不意味著這一次呼叫僅僅只發生一次邏輯呼叫。比如一次簡單的下訂單請求,首先會查詢資料庫有無庫存,然後修改對應商品的庫存數量,同時增加一條訂單資訊,往MQ中投遞一條通知簡訊,等等。不僅僅是呼叫多個dao層,而且可能會有MQ的相關操作。一次請求不等於一次邏輯呼叫。而多次邏輯呼叫意味著多張資料庫表的資料會得到更新。假如其中某一個dao層或者cache層發生異常,這一次呼叫應該是失敗,並且由資料庫保證回滾的。第二個理由是,controller由於是跟web容器繫結在一起的,由容器決定每一次請求是否開啟執行緒進行響應,而且執行緒的容量也跟容器息息相關。所以儘可能保證執行緒高效,而事務會開啟另外一個單獨的執行緒,執行緒排程之間如果出現異常,那麼事務可能會失控。第三個理由是,controller可能只針對restfull介面,如果對於常規RPC介面,直接開放service的,那麼事務會直接不生效導致業務不滿足。

       通常情況下,一般的研發團隊都會把事務攔截放在service來進行出來,而service中的每一個方法由spring aop進行事務保證。只要在一個事務環境中,一定能保證ACID,service層的方法中要把一次方法的呼叫放在一起,可以是直接呼叫dao層,也可以是呼叫service層。不管是從將來的可擴充套件性(即使直接將service暴露給rpc介面,事務依然生效),還是單一職責(一次操作由一個方法進行保障)。推薦將事務操作都放在service層進行統一處理。

        對於一些小專案,對於controller和service沒有特別明顯的區別,甚至有的模組可以直接把service的邏輯實現直接放在controller中,這也不是什麼大問題,在專案初期,使用人數不多,業務夠簡單,邏輯清晰的情況下,這也無傷大雅,如果隨著專案的複雜度上升,業務邏輯加大,建議還是把事務的控制從controller層移到servcie。

       小調查,各位朋友所在的開發專案組,對於事務是放在哪一層呢?可以留言交流。

相關文章