所有文章
https://www.cnblogs.com/lay2017/p/12078232.html
正文
一、什么是事務?
概念性的東西通常都顯得抽象、晦澀,包羅萬象但似乎很難一下子抓到要點。為此,我們先來看一個比較典型的例子:銀行轉賬
市民王先生到銀行轉賬1000元給老李,王先生的賬戶里現有10000元,老李的賬戶恰好也有10000元。銀行將從王先生賬戶扣除1000元:10000-1000=9000,然后給老李的賬戶加上1000元:10000+1000=11000。
王先生的賬戶剩余:9000元
老李的賬戶剩余:11000元
我們來看看,這里有兩個步驟1)從王先生賬戶扣錢;2)給老李賬戶加錢;我們做一個問題假設,銀行櫃員從王先生的賬戶扣完錢以后,忘記給老李的賬戶加錢了。那么會出現什么結果?
王先生的賬戶剩余:10000-1000=9000
老李的賬戶剩余:10000+0=10000
很明顯,老李沒有收到王先生的錢。而王先生也很委屈,已經扣完錢了啊。這反應了一個比較嚴重的問題,就是當你要處理的事情被分為了很多個步驟,這些步驟可能處理成功,可能失敗,甚至可能完全忘了處理。如果都處理成功也沒什么問題。如果都處理失敗,我們也可以當作什么都沒有發生過。唯獨有的步驟成功,有的步驟失敗的時候比較煩人了。就會出現上面的例子中的問題。
這時候我們會想,既然都成功或者都失敗都行,我們能不能讓多個步驟的處理像處理一個步驟一樣簡單點要么全部成功,要么全部失敗呢?如上例子中,要么王先生扣完1000以后也給老李加上10000,要么王先生沒扣錢,老李也沒加錢,最多老李發現錢沒到賬會再要求王先生轉一次。
我們可以給這兩個步驟取個名字:轉賬,轉賬包含了從一個賬戶扣錢,給另一個賬戶加錢兩個步驟,兩個步驟同時成功或者同時失敗。
拋開上面的例子,我們所要處理的其它事情會不會也存在轉賬這樣的問題呢?當然!那么,我們把這些要做的事情像轉賬一樣取個共同的名字,就叫做"事務"。
到這里,我們給事務下一個定義吧。
事務就是你要做的事情,通常這件事會包含多個步驟。
事務中的多個步驟處理起來就像處理一個步驟一樣,要么全部成功,要么全部失敗。
事務解決了多個步驟部分成功,部分失敗所帶來的不一致問題。
所以,事務的誕生是其實是為了解決問題,這種技術手段主要解決了"一致性"問題。
二、事務的ACID四個特征
任何存在的真實的亦或者是虛擬的人事物都有其特征,我們通過其特征來識別它與其它人事物之間的區別。
那么,事務這種技術手段存在什么特征呢?
事務的特征,我們簡稱為ACID。它們分別是什么呢?
A:atomicity 原子性
C:consistency 一致性
I:isolation 隔離性
D:durability 持久性
一致性
我們總說事務具備一致性,但是個人認為它並非事務本身的特性。而是事務這種技術手段,維護了所要處理的對象的一致性。比如說mysql中的數據的一致性,mysql的事務技術維護了mysql內部存儲的數據具備一致性。
再來,理解一致性,我們先提一下我們以前學過的"能量守恆定律"。
能量守恆定律是指一個封閉或者孤立的系統中,總的能量是保持不變的。
不會憑空產生,也不會憑空消失,只能從一個物體傳遞給另一個物體,且能量的形式可以相互轉換。
和我們賴以生存的自然界一樣,我們程序員所構建的系統也是一個"能量守恆"的世界。我們的代碼,我們的數據都是這個世界的一塵一土,不會憑空消失,不會憑空產生。
而事務技術,就是為了讓這個程序世界能夠像真實世界一樣做到"能量守恆"。我們簡稱其為"事務的一致性"。
似乎從這里感受到了早年看書的時候經常忽略的話,"編程就是對現實世界進行建模",我們可以像上帝一樣在這里創造萬物,萬物生生不息。
原子性
原子是什么?原子是指在化學的定義上一個不可再分割的基本微粒,它是構成一般物質的最小單元。
以上定義指的是一個客觀存在的事物由原子構成。那,如果是一個行為,它具備原子特性是什么概念呢?
首先,原子不可分割,是最小的單元。所以,一個行為具備原子特性就不能被再拆分成多個步驟,我們把這個行為看成一個最小單元。步驟全部完成,或全部沒開始。還是不好理解?我們舉個例子
你一步一步地走路回家,這時候你突然發現在你正要落腳地位置有一坨不可言狀地東西。你感到惡心,所以腳到一半就停住了,然后越過它。
很明顯,"走一步"這個行為並沒有形成原子性。想象一下,如果成為了一個具備原子性的行為,那么即使你的大腦發現有一坨東西,就不能夠腳停在半空中,再越過它。你必須踩下去,或者腳收回來(這叫回滾),重新走一步。
這里的原子性只是表達了事務的一種特性,它旨在關注行為整體,而忽略行為的具體步驟。保證事情全部完成,或者全部像沒開始一樣。
持久性
持久性在定義中是指:事務一旦被提交,它產生的改變就是永久性的。即使發生故障也不應該影響改變的成功與否。
這里注意"提交"這兩個事情,我們通俗地說就是一件事完成以后,它產生的影響就是既存事實。不能因為其它因素導致這個事實不存在了。好像還是不好理解,舉例說明
銀行查賬的時候發現,A轉賬給B,B轉賬給C。這里有兩個轉賬事務產生了。那么如果銀行因為停電,A轉賬給B這個事務卻不再是既存事實,銀行查賬只發現B轉賬給C這一筆,就會奇怪為什么B的賬戶了多了一筆錢,而丟失了本該是A轉賬過來的事務結果?
持久性,代表了一種不可改變的既存事實,也就是我們常說的歷史無法改變,只能看向未來。這樣看來,如果丟了秦始皇大一統的歷史,整個后面的朝代還怎么存在呢?所以,有持久性才能維持世界的一致性。
隔離性
前面的內容里,我們的思維基本上處於思考單線程的狀態。而事務在其復雜的使用場景中卻不得不考慮並發場景。因此,我們在這里提出"隔離性"。
這里,編一個小故事,噢~不!講一個小故事,故事的名稱叫《老王請客》
老王是一個地中海格子衫的中年男子,他有一個小金庫。前幾天被老婆發現以后,老王大義凜然地說:"我王某的小金庫老婆隨時可以取用"。看着老王飄逸的發型,他老婆終究心軟了。
第一次
老王決定請朋友吃飯慶祝一下,看了一下小金庫,還有1000塊錢,於是高興地跑去餐廳吃飯。
他老婆心血來潮,逛街地時候也看了看小金庫,發現有1000塊錢,於是決定獎勵自己一雙鞋子,很快就付款了。
老王吃完飯,付款的時候,發現卡里沒錢了,很不幸地加入了洗盤子的隊伍。
read uncommitted級別,不考慮並發問題,只要數據被update,那么不管事務是否提交都會被其它事務讀取到。
如果事務產生回滾,讀取到該事務update以后數據的事務就會出現臟讀問題。
一般不會采用這個級別,除非完全不考慮並發問題。
第二次
老王吸取教訓,決定只能用卡才能取自己小金庫的錢,覺得自己真是太聰明了。
於是,老王決定再次請客,挽回顏面。他謹慎地看了看還有1000塊錢,高興地去了餐廳。
他老婆看到了這一幕,想要戲弄他一下。看了看小金庫還有1000塊錢,准備再獎勵給自己一件小黑裙。但是,她發現沒有卡不能取錢。於是氣呼呼地等老王回家。
老王吃完並付款成功,這一次終於不用洗盤子了。
他老婆越等越生氣,再看了看小金庫,發現錢已經被花掉了,小黑裙得不到了。於是決定讓老王承擔家里洗盤子的工作。
read committed級別,一個事務中update數據,在事務被提交以后才會被其它事務看見。故事中的卡就像是一個排它鎖,限制了同時update。
有效地解決了並發地時候出現臟讀問題。
但是,也會導致兩次讀取的數據卻不一致的問題,出現同一個事務中不可重復讀問題。
第三次
由於上一次戲弄老王失敗,他老婆決定先下手為強。看了看小金庫又有1000塊錢,於是決定帶走卡,買回上次留下遺憾的小黑裙。
老王起床發現老婆不在,准備看看小金庫,這一次小金庫更加被限制的沒有卡余額都看不了。老王一臉揪心地坐在電腦前,打消了請客地念頭,默默地開始玩游戲。
終於,他老婆高興地回來了。老王接過卡,一查零頭都不剩,默默地去洗碗了。
repeatable read級別,同一個事務中能夠重復讀取到相同的數據。也因此,repeatable read將比read committed更加地排他。
故事中基本上都是update數據,但是如果產生insert數據的情況,也就是家里突然多出一張卡。那么在同一個事務中就會出現"幻讀"的問題。
第四次
老王經歷多次失敗,決定每次等老婆用完以后再開始考慮是否請客吃飯。至於卡里剩多少錢,聽天意了。
這樣,就不用因為沒錢被留下洗盤子。也不用讓老婆等太久而被罰洗盤子。更不用一大早起床,卻揪心等待看看自己的小金庫還剩多少。
serializable級別意味着事務之間串行化,也很輕易地解決了臟讀、不可重復讀、幻讀地問題。
但是串行化也意味着效率非常低,並發度非常低。通常也不使用這種級別。
這里對隔離級別做一個小結
隔離級別分為:1)read uncommitted 2)read committed 3)repeatable read 4)serializable
從左到右級別越高,並發能力越低。所以,一般數據庫會折中選擇read committed或者repeatable read作為默認的隔離級別。
在二者中做選擇也就是說"不可重復讀"問題是否需要被考慮,這應該根據業務場景分析。
比如:
1)我要獲取12:00:00這個時間的精准在線人數,而在事務A第一次讀取時1000人,這時事務B提交了一次修改變成了1001人,事務A第二次讀取的時候變成了1001人,這就影響了精准度。可以選擇repeatable read
2)而,如果我不關心精准度,我只是考慮每天12:00:00獲取一下當前的在線人數。那么兩次讀取不一致並沒有什么影響。可以選擇read committed
總結
到這里,關於事務的基本概念就講完了。我們從了解什么是事務?到理解ACID的四個特性。
最麻煩的隔離級別也坎坷地通過一個故事的方式理解,不過隔離級別這個東西現在還不需要太糾結它的實現。比如,你思考的時候不停地聯系到數據庫的隔離級別或者spring框架的隔離級別的實現細節,這可能加重理解它的復雜程度。
后續的文章會繼續討論,加深理解。這里,我們理解在不同隔離級別下,事務之間什么時候能讀取到對方update的數據,每個級別還有什么遺留問題需要考慮,並發性如何即可。
最后,我們可以將事務理解為維護"程序世界"數據一致性的技術手段,讓系統"能量守恆"。