Hibernate 性能之事務與并發(fā)
1. 前言
本節(jié)課和大家一起聊一聊事務和并發(fā)。通過本節(jié)課程的學習,你將了解到:
- 什么是事務 ;
- 事務的隔離機制。
2. 事務
什么是事務?
一講到事務,就有一個典型的案例:轉賬。
轉賬業(yè)務涉及到 2 個賬號的變更。例如 A 現(xiàn)在要把自己賬戶上僅有的 100 元大鈔轉給自己的好朋友 B。
轉賬過程中至少要涉及到兩條更新語句:
- A 賬面上的錢減少,使用更新語句實現(xiàn):
Update 賬號表 set 我的錢=我的錢-100 where 賬號擁有人=A
- B 賬戶上的錢增多,使用更新語句實現(xiàn):
Update 賬號表 set 我的錢=我的錢+100 where 賬號擁有人=B
如果、萬一這兩條更新語句中有一條沒有執(zhí)行成功,會發(fā)生什么情況了?
-
發(fā)生在 A 身上的更新語句沒有成功,B 的更新語句成功了。也就是說 A 賬面上的錢沒有減少,B 賬面上的錢卻增加了。天呀!這是何等好事,關鍵是這錢是從哪里來的,左想右想,看來只能是銀行里來的,但是,你覺得銀行會做這種傻事嗎?不會!那銀行又是如何保證不讓這種事情發(fā)生了。
-
發(fā)生在 B 身上的更新語句沒有成功,A 的更新語句成功了。也就是說 A 的錢減少了,但是 B 沒有增加。傻眼了吧,莫名其妙的錢就不見了。錢去哪兒了?你會讓這種事情發(fā)生嗎?也不會,你會投訴銀行的。
當然,如果這 2 條 SQL 語句執(zhí)行成功或者失敗,則不會發(fā)生任何損失,可見,咱們必須控制這兩條 SQL 語句要么都成功,要么都失敗。
對!這就是事務。
所謂事務,就是把一個業(yè)務邏輯當成一個邏輯整體單元,其中的執(zhí)行代碼要么一起成功,但凡執(zhí)行過程中出現(xiàn)了某些錯誤,就恢復到最原始的狀態(tài)。
轉賬就是一個業(yè)務邏輯,整個業(yè)務邏輯中至少包括 2 條 SQL 語句,這 2 條 SQL 語句互為依靠,彼此脫單對業(yè)務邏輯沒有任何意義。所以,必須當成一個整體看待。
一個事務單元有 4 個特性,也就是事務的 ACID 特性:
- 原子性(atomicity): 表示一個事務內(nèi)的所有操作是一個整體,要么全部成功,要么全部失??;
- 一致性(consistency): 表示一個事務內(nèi)有一個操作失敗時,所有的更改過的數(shù)據(jù)都必須回滾到修改前的狀態(tài);如前面轉賬案例,轉賬前 A 和 B 加起來有多少錢,無論轉賬是否成功,最后 A 和 B 加起來的錢應該和前面相等;
- 隔離性(isolation): 事務查看數(shù)據(jù)時數(shù)據(jù)所處的狀態(tài),要么是另一并發(fā)事務修改它之前的狀態(tài),要么是另一事務修改它之后的狀態(tài),事務不會查看中間狀態(tài)的數(shù)據(jù);
- 持久性(durability): 事務完成之后,它對于系統(tǒng)的影響是永久性的。事務結束后,就沒有什么后悔藥了。
4 個特性中除了隔離性都比較好理解。所以,剩下的篇幅中,咱們好好聊一聊隔離性。要講透隔離性,肯定就離不開并發(fā)概念。
什么是并發(fā)?
并發(fā)是計算機中的一個概念,但是在現(xiàn)實生活中還是能找到一個能類比的例子。
火車上,2個人同時去一間洗手間,這個過程可以稱其為并發(fā)。換成計算機概念就是說兩段邏輯代碼同時使用同一個資源。當然,這里只是從宏觀上理解并發(fā)。
老公和老婆共用一個賬號,大家需要錢時都從這個賬號上取,可假設老公和老婆就是兩個事務,這 2 個事務如果同時取錢時,就必須隔離,否則就會出現(xiàn)麻煩。
2 個事務同時進入這個賬號,一查看,很高興,賬面上有錢,于是都想取出這 1000 塊錢。銀行肯定只能讓一個事務成功,要不然銀行就虧大了。
這便是事務的隔離機制,當一個事務對一個資源進行操作時,必須隔離另一個事務對其進行相關操作。
但是,如果隔離的太嚴格了,事務之間就如同排隊,需要一個一個來,將會降低系統(tǒng)的響應時間,使用者會認為,切!這系統(tǒng)設計的夠糟糕的,睡了一覺,還沒有響應。
如果隔離的太寬松了,受事務之間的影響,會發(fā)生數(shù)據(jù)的異常。
所以在 JDBC 中一般會提供多種隔離機制,讓開發(fā)者根據(jù)需要進行選擇。
事務隔離級別從低到高:
- 讀取未提交(Read Uncommitted);
- 讀取已提交 (Read Committed);
- 可重復讀(Repeatable Read);
- 序列化(serializable)。
如何選擇,當然是需要根據(jù)業(yè)務需要進行設定。
不同的隔離機制下,并發(fā)的事務之間會發(fā)生一些什么樣的事情?
3. 隔離與并發(fā)
3.1 讀取未提交(Read Uncommitted)
字面上理解,可以讀取沒有提交的數(shù)據(jù),這是最低的事務隔離級別:
- 讀事務不會阻塞讀事務和寫事務,寫事務也不會阻塞讀事務,但是會阻塞寫事務;
- 寫事務不阻塞讀事務,可以讀取未提交的數(shù)據(jù)。
后果是,會出現(xiàn)臟讀現(xiàn)象。
如果財務工作人員在更新公司員工工資時,不小心把 A 同事的原工資 2000 改成了 8000,但馬上發(fā)現(xiàn)了錯誤,立馬更正過來。改過就好,不傷大雅。
問題是,如果在財務工作人員修改時,A 同事恰好也在查看自己的賬號,因為可看到別的事務沒有提交的數(shù)據(jù),天降財富呀,眼睛沒花吧,工資漲成 8000 元了。
如果晚上回家告訴了老婆,第二天再一查,工資還是 2000 。請問下班后該如何向老婆交待!
這就叫臟讀。當然,并不是沒有解決的辦法。
臟讀解決方案:在財務人員的事務提交前,任何其他事務不可讀取其修改過的值,則可以避免 A 同事回家挨罵的悲劇。
3.2 讀取已提交 (Read Committed)
這個比前面的要嚴格點,只能讀已經(jīng)提交的,就不會出現(xiàn)臟讀情況。
- 寫事務會阻塞讀事務和寫事務,但是讀事務不會阻塞讀事務和寫事務。
- 讀事務不阻塞寫事務
但是有可能造成不可重復讀。
如果 A 同事聽說老板要給自己漲工資,查看了一下賬號,唉!還是只有 1000 元,心情好糟糕。
此時財務人員對 A 同事的賬號進行了修改了,變成 2000,并提交了事務。A 同事再次讀取時,工資變成 2000。此時就不知道是自己眼花還是在做夢。這就是不可重復,意思就是說,在這種隔離下,你最好不要有事沒事的反復讀,可能會出現(xiàn)前后讀出來的數(shù)據(jù)不一致的情況。
3.3 可重復讀(Repeatable Read)
- 讀事務會阻塞寫事務,但是讀事務不會阻塞讀事務,寫事務會阻塞寫事務和讀事務;
- 讀事務不阻塞讀事務 (針對的是記錄而不是表)。
可能會造成幻讀問題:
幻讀是針對事務期間所插入的新數(shù)據(jù)而言。
如財務人員統(tǒng)計一下所有工資為 1000 的員工,這時人事向員工表插入了一條新記錄,工資也是 1000 。財務人員再次讀取所有工資為 1000 的員工, 共讀取到了 11 條記錄。
可采用對表加鎖的方式避免幻讀的事情發(fā)生,一般情況下不會過于關心是否會發(fā)生幻讀。
3.4 序列化(serializable)
這算是最嚴格的隔離級別,如果設置成這個級別,就不會出現(xiàn)以上所有的問題題(臟讀,不可重復讀,幻影讀)。但是,性能極低,一般不用!
4. 小結
好了,到了該總結的時候了。本節(jié)課向大家介紹了事務機制,重點講解了事務的隔離機制。
隔離策略,就如同交通法則,壓一點點實線;紅燈時,車頭出了暫停線一點點都要罰錢,大家開車就只能小心翼翼啦, 交通的暢通性就會降低。
但是,如果太放寬,就容易發(fā)生交通事故。
所以呀!隔離機制要根據(jù)實際情況進行選擇。