什么是事務
事務是指是程序中一系列嚴密的邏輯操作,而且所有操作必須全部成功完成,否則在每個操作中所作的所有更改都會被撤消。可以通俗理解為:就是把多件事情當做一件事情來處理,好比大家同在一條船上,要活一起活,要完一起完 。
事物的四個特性(ACID)
● 原子性(Atomicity):操作這些指令時,要么全部執行成功,要么全部不執行。只要其中一個指令執行失敗,所有的指令都執行失敗,數據進行回滾,回到執行指令前的數據狀態。
eg:拿轉賬來說,假設用戶A和用戶B兩者的錢加起來一共是20000,那么不管A和B之間如何轉賬,轉幾次賬,事務結束后兩個用戶的錢相加起來應該還得是20000,這就是事務的一致性。
● 一致性(Consistency):事務的執行使數據從一個狀態轉換為另一個狀態,但是對於整個數據的完整性保持穩定。
● 隔離性(Isolation):隔離性是當多個用戶並發訪問數據庫時,比如操作同一張表時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作所干擾,多個並發事務之間要相互隔離。
即要達到這么一種效果:對於任意兩個並發的事務T1和T2,在事務T1看來,T2要么在T1開始之前就已經結束,要么在T1結束之后才開始,這樣每個事務都感覺不到有其他事務在並發地執行。
● 持久性(Durability):當事務正確完成后,它對於數據的改變是永久性的。
eg: 例如我們在使用JDBC操作數據庫時,在提交事務方法后,提示用戶事務操作完成,當我們程序執行完成直到看到提示后,就可以認定事務以及正確提交,即使這時候數據庫出現了問題,也必須要將我們的事務完全執行完成,否則就會造成我們看到提示事務處理完畢,但是數據庫因為故障而沒有執行事務的重大錯誤。
並發事務導致的問題
在許多事務處理同一個數據時,如果沒有采取有效的隔離機制,那么並發處理數據時,會帶來一些的問題。
● 第一類丟失更新:撤銷一個事務時,把其他事務已提交的更新數據覆蓋。
eg:小明去銀行櫃台存錢,他的賬戶里原來的余額為100元,現在打算存入100元。在他存錢的過程中,銀行年費扣了5元,余額只剩95元。突然他又想着這100元要用來請女朋友看電影吃飯,不打算存了。在他撤回存錢操作后,余額依然為他存錢之前的100元。所以那5塊錢到底扣了誰的?
● 臟讀:臟讀是指在一個事務處理過程里讀取了另一個未提交的事務中的數據。
eg:小明的銀行卡余額里有100元。現在他打算用手機點一個外賣飲料,需要付款10元。但是這個時候,他的女朋友看中了一件衣服95元,她正在使用小明的銀行卡付款。於是小明在付款的時候,程序后台讀取到他的余額只有5塊錢了,根本不夠10元,所以系統拒絕了他的交易,告訴余額不足。但是小明的女朋友最后因為密碼錯誤,無法進行交易。小明非常郁悶,明明銀行卡里還有100元,怎么會余額不足呢?(他女朋友更郁悶。。。)
● 幻讀也叫虛讀:一個事務執行兩次查詢,第二次結果集包含第一次中沒有或某些行已經被刪除的數據,造成兩次結果不一致,只是另一個事務在這兩次查詢中間插入或刪除了數據造成的。幻讀是事務非獨立執行時發生的一種現象。
eg:例如事務T1對一個表中所有的行的某個數據項做了從“1”修改為“2”的操作,這時事務T2又對這個表中插入了一行數據項,而這個數據項的數值還是為“1”並且提交給數據庫。而操作事務T1的用戶如果再查看剛剛修改的數據,會發現還有一行沒有修改,其實這行是從事務T2中添加的,就好像產生幻覺一樣,這就是發生了幻讀。
● 不可重復讀:一個事務兩次讀取同一行的數據,結果得到不同狀態的結果,中間正好另一個事務更新了該數據,兩次結果相異,不可被信任。
eg:例如事務T1在讀取某一數據,而事務T2立馬修改了這個數據並且提交事務給數據庫,事務T1再次讀取該數據就得到了不同的結果,發送了不可重復讀。
Tips:不可重復讀和臟讀的區別:臟讀是某一事務讀取了另一個事務未提交的臟數據,而不可重復讀則是讀取了前一事務提交的數據。
Tips:幻讀和不可重復讀都是讀取了另一條已經提交的事務(這點就臟讀不同),所不同的是不可重復讀查詢的都是同一個數據項,而幻讀針對的是一批數據整體(比如數據的個數)。
● 第二類丟失更新:是不可重復讀的特殊情況。如果兩個事物都讀取同一行,然后兩個都進行寫操作,並提交,第一個事物所做的改變就會丟失。
eg:小明和女朋友一起去逛街。女朋友看中了一支口紅,(對,女朋友就是用來表現買買買的)小明大方的掏出了自己的銀行卡,告訴女朋友:親愛的,隨便刷,隨便買,我坐着等你。然后小明就坐在商城座椅上玩手機,等着女朋友。這個時候,程序員的聊天群里有人推薦了一本書,小明一看,哎呀,真是本好書,還是限量發行呢,我一定更要買到。於是小明趕緊找到購買渠道,進行付款操作。而同時,小明的女朋友也在不亦樂乎的買買買,他們同時進行了一筆交易操作,但是這個時候銀行系統出了問題,當他們都付款成功后,卻發現,銀行只扣了小明的買書錢,卻沒有扣去女朋友此時交易的錢。哈哈哈,小明真是太開心了!
數據庫事務的隔離級別
事務的隔離級別有4種,由低到高分別為Read uncommitted 、Read committed 、Repeatable read 、Serializable 。而且,在事務的並發操作中可能會出現臟讀,不可重復讀,幻讀。下面通過事例一一闡述它們的概念與聯系。
Read uncommitted(最低級別,任何情況都無法保證。)
讀未提交,顧名思義,就是一個事務可以讀取另一個未提交事務的數據。
eg:老板要給程序員發工資,程序員的工資是3.6萬/月。但是發工資時老板不小心按錯了數字,按成3.9萬/月,該錢已經打到程序員的戶口,但是事務還沒有提交,就在這時,程序員去查看自己這個月的工資,發現比往常多了3千元,以為漲工資了非常高興。但是老板及時發現了不對,馬上回滾差點就提交了的事務,將數字改成3.6萬再提交。
Analyse:實際程序員這個月的工資還是3.6萬,但是程序員看到的是3.9萬。他看到的是老板還沒提交事務時的數據。這就是臟讀。
那怎么解決臟讀呢?Read committed!讀提交,能解決臟讀問題。
Read committed(可避免臟讀的發生。)
讀提交,顧名思義,就是一個事務要等另一個事務提交后才能讀取數據。
eg:程序員拿着信用卡去享受生活(卡里當然是只有3.6萬),當他埋單時(程序員事務開啟),收費系統事先檢測到他的卡里有3.6萬,就在這個時候!!程序員的妻子要把錢全部轉出充當家用,並提交。當收費系統准備扣款時,再檢測卡里的金額,發現已經沒錢了(第二次檢測金額當然要等待妻子轉出金額事務提交完)。程序員就會很郁悶,明明卡里是有錢的…
Analyse:這就是讀提交,若有事務對數據進行更新(UPDATE)操作時,讀操作事務要等待這個更新操作事務提交后才能讀取數據,可以解決臟讀問題。但在這個事例中,出現了一個事務范圍內兩個相同的查詢卻返回了不同數據,這就是不可重復讀。
那怎么解決可能的不可重復讀問題?Repeatable read !
Repeatable read(可避免臟讀、不可重復讀的發生。)
重復讀,就是在開始讀取數據(事務開啟)時,不再允許修改操作
eg:程序員拿着信用卡去享受生活(卡里當然是只有3.6萬),當他埋單時(事務開啟,不允許其他事務的UPDATE修改操作),收費系統事先檢測到他的卡里有3.6萬。這個時候他的妻子不能轉出金額了。接下來收費系統就可以扣款了。
Analyse:重復讀可以解決不可重復讀問題。寫到這里,應該明白的一點就是,不可重復讀對應的是修改,即UPDATE操作。但是可能還會有幻讀問題。因為幻讀問題對應的是插入INSERT操作,而不是UPDATE操作。
什么時候會出現幻讀?
eg:程序員某一天去消費,花了2千元,然后他的妻子去查看他今天的消費記錄(全表掃描FTS,妻子事務開啟),看到確實是花了2千元,就在這個時候,程序員花了1萬買了一部電腦,即新增INSERT了一條消費記錄,並提交。當妻子打印程序員的消費記錄清單時(妻子事務提交),發現花了1.2萬元,似乎出現了幻覺,這就是幻讀。
那怎么解決幻讀問題?Serializable!
Serializable(可避免臟讀、不可重復讀、幻讀的發生。) 序列化
Serializable 是最高的事務隔離級別,在該級別下,事務串行化順序執行,可以避免臟讀、不可重復讀與幻讀。但是這種事務隔離級別效率低下,比較耗數據庫性能,一般不使用。
Tips:大多數數據庫默認的事務隔離級別是Read committed,比如Sql Server , Oracle。
Mysql的默認隔離級別是Repeatable read。
Tips:隔離級別的設置只對當前鏈接有效。對於使用MySQL命令窗口而言,一個窗口就相當於一個鏈接,當前窗口設置的隔離級別只對當前窗口中的事務有效;對於JDBC操作數據庫來說,一個Connection對象相當於一個鏈接,而對於Connection對象設置的隔離級別只對該Connection對象有效,與其他鏈接Connection對象無關。
Tips:設置數據庫的隔離級別一定要是在開啟事務之前。