前言
在互聯(lián)網(wǎng)數(shù)據(jù)庫的使用中乌企,對于那些電商和金融網(wǎng)站,最關注的內容毫無疑問就是數(shù)據(jù)庫事務,因為對于人們商品的交易和庫存以及金融產(chǎn)品的金額睡雇,是不允許發(fā)生錯誤的。但是它們面臨的問題是饮醇,熱門產(chǎn)品或金融產(chǎn)品上線銷售瞬間可能面對的高并發(fā)的場景它抱。那么在Spring中采用哪些事務機制處理這些高并發(fā)場景的呢?
Spring事務管理
常見的Spring事務管理有以下兩種:
1. 編程式事務管理
- 編程式事務管理需要顯示的調用beginTransaction()朴艰、rollback()观蓄、commit()等相關事務的處理方法混移,操作比較復雜,不推薦侮穿。
2.聲明式事務管理
- 基于AOP技術實現(xiàn)的聲明式事務管理歌径,實質就是:在方法執(zhí)行前后進行攔截,然后在目標方法執(zhí)行前創(chuàng)建并加入事務亲茅,在目標方法執(zhí)行后根據(jù)結果進行事務的回滾或者提交沮脖。
- 聲明式事務管理的實現(xiàn)有兩種:
- 基于XML配置文件的實現(xiàn);
2.在業(yè)務方法上加上@Transactional注解芯急,將事務規(guī)則應用到業(yè)務邏輯中。
對于聲明式事務驶俊,是使用@Transactional進行標注的娶耍,這個注解可以標注在類或方法上,當它標注在類上時饼酿,代表這個類的所有公共的(public)非靜態(tài)的方法都將啟動事務功能榕酒。在@Transactional中,還允許配置許多的屬性故俐,如事務的傳播行為和隔離級別想鹰,以及異常類型,從而確定方法發(fā)生什么異常下回滾事務什么異常下不回滾事務等药版。這些配置內容辑舷,是在Spring IoC容器在加載時就會將這些配置信息解析出來,然后把這些信息存到事務定義器(TransactionDefinition接口的實現(xiàn)類)里槽片,記錄哪些類或者方法需要啟動事務功能何缓,采取什么策略去執(zhí)行事務。在這個過程中还栓,我們需要做的就是給需要事務的類或方法中加上@Transactional注解和配置其屬性而已碌廓。
有了@Transactional的配置,Spring就會知道在哪里啟動事務機制剩盒,其約定流程如下圖所示:
3.隔離級別
當前互聯(lián)網(wǎng)應用時刻面臨著高并發(fā)的環(huán)境谷婆,如商品庫存,時刻都是多個線程共享的數(shù)據(jù)辽聊,這樣就會在多線程的環(huán)境中扣減商品庫存纪挎。對于數(shù)據(jù)庫而言,就會出現(xiàn)多個事務同時操作同一記錄的情況身隐,這樣會引起數(shù)據(jù)出現(xiàn)不一致的情況廷区,便是數(shù)據(jù)的丟失更新(Lost Update)問題。
數(shù)據(jù)庫事務的知識
數(shù)據(jù)庫事務具有以下4個基本特性贾铝,也就是著名的ACID隙轻。
- Atomic(原子性):事務中包含的操作被看做一個整體的業(yè)務單元埠帕,這個業(yè)務單元的操作要么全部成功,要么全部失敗玖绿,不會出現(xiàn)部分成功敛瓷,部分失敗的現(xiàn)象。
- Consistency(一致性):事務在完成時斑匪,必須使所有的數(shù)據(jù)都保持一致狀態(tài)呐籽,在數(shù)據(jù)庫中所有的修改都基于事務,保證了數(shù)據(jù)的完整性蚀瘸,
- Isolation(隔離性):可能多個應用程序線程同時訪問同一數(shù)據(jù)狡蝶,這樣數(shù)據(jù)庫同樣的數(shù)據(jù)在各個不同的事務中被訪問,這樣會產(chǎn)生丟失更新贮勃,為了壓制丟失更新的產(chǎn)生贪惹,數(shù)據(jù)庫定義了隔離級別的概念,通過它的選擇寂嘉,可以在不同程度上壓制數(shù)據(jù)丟失更新的產(chǎn)生奏瞬,因為互聯(lián)網(wǎng)的應用常常面對高并發(fā)的場景,所以隔離性是需要掌握的重點內容泉孩。
- Durability(持久性):事務結束后硼端,所有的數(shù)據(jù)都會固定到一個地方,如保存到磁盤中寓搬,即使斷電重啟后也可以提供給應用程序訪問珍昨。
第一類丟失更新
時刻 | 事 務 1 | 事 務 2 |
---|---|---|
T1 | 初始庫存100 | 初始庫存100 |
T2 | 扣減庫存,余99 | —— |
T3 | —— | 扣減庫存句喷,余99 |
T4 | 提交事務曼尊,庫存變?yōu)?9 | |
T5 | 回滾事務,庫存100 |
可以看到脏嚷,T5時刻事務回滾骆撇,導致原本庫存為99的變?yōu)榱?00,顯然事務2的結果就丟失了父叙,這就是一個錯誤的值神郊,類似的,對于這樣一個事務提交而引發(fā)的數(shù)據(jù)不一致的情況趾唱,我們稱為第一類丟失更新涌乳。
第二類丟失
時刻 | 事 務 1 | 事 務 2 |
---|---|---|
T1 | 初始庫存100 | 初始庫存100 |
T2 | 扣減庫存,余99 | —— |
T3 | —— | 扣減庫存甜癞,余99 |
T4 | —— | 提交事務夕晓,庫存為99 |
T5 | 提交事務,庫存變?yōu)?9 | —— |
注意在T5時刻提交的事務悠咱。因為在事務1中蒸辆,無法感知事務2 的操作征炼,這樣它就不知道事務2已經(jīng)做了修改,因此它依舊認為這只是發(fā)生了一筆業(yè)務躬贡,所以庫存更新變成了99谆奥,而這個結果又是一個錯誤的結果。這樣T5時刻事務1提交的事務拂玻,就會引發(fā)事務2提交結果的丟失酸些,我們把這樣的多個事務的提交引發(fā)丟失更新的稱為第二類丟失更新。
為處理第二類丟失更新引發(fā)的錯誤檐蚜,提出了事務的隔離級別魄懂,常見的隔離級別有以下4種:
- 未提交讀
- 讀寫提交
- 可重讀
- 串行化
下面我們來一一細說這四個隔離級別的作用
1.未提交讀
未提交讀(read uncommitted)是最低的得力級別,其含義是允許一個事務讀取另一個事務沒有提交的數(shù)據(jù)闯第。未提交讀是一種危險的隔離級別逢渔。所以我們一般在實際的開發(fā)中應用不廣,但是它的優(yōu)點在于并發(fā)能力高乡括,適合那些對數(shù)據(jù)一致性沒有要求而追求高并發(fā)的場景,它的最大壞處是出現(xiàn)臟讀智厌。
臟讀現(xiàn)象
時刻 | 事 務 1 | 事 務 2 | 備 注 |
---|---|---|---|
T0 | ....... | ........ | 商品庫存初始化為2 |
T1 | 讀取庫存為2 | ||
T2 | 扣減庫存 | 庫存為1 | |
T3 | 扣減庫存 | 庫存為0诲泌,讀取事務1為提交的數(shù)據(jù) | |
T4 | 提交事務 | 庫存保存為0 | |
T5 | 回滾事務 | 因為第一類丟失更新已經(jīng)克服,所以不會回滾為2铣鹏,庫存為0敷扫,結果錯誤 |
因為采用未提交讀,所以事務2可以讀取事務1為提交的庫存數(shù)據(jù)為1诚卸,這里當它扣減庫存后則數(shù)據(jù)為0葵第,然后它提交了事務,庫存就變成了0合溺,而事務1在T5時刻回滾事務卒密,因為第一類丟失更新已經(jīng)被克服,所以它不會將庫存回滾到2棠赛,那么最后的結果就變成了0哮奇,所以就出現(xiàn)了這樣的錯誤,臟讀一般是比較危險的隔離級別睛约,在我們實際應用中采用的不多鼎俘。