什么是事務
事務(Transaction):訪問并可能更新數(shù)據(jù)庫中各種數(shù)據(jù)項的一個程序執(zhí)行單元(unit)倚聚,它通常由高級數(shù)據(jù)庫操縱語言或編程語言(如SQL,C++或Java)書寫的用戶程序的執(zhí)行所引起啡氢。當在數(shù)據(jù)庫中更改數(shù)據(jù)成功時,在事務中更改的數(shù)據(jù)便會提交术裸,不再改變倘是。否則,事務就取消或者回滾袭艺,更改無效搀崭。
事務解釋:指要做的或所做的事情
事務本質:一系列操作
事務特性:事務是恢復和并發(fā)控制的基本單位。
例如 網(wǎng)上購物匹表,其交易過程至少包括以下幾個步驟的操作:
- 更改客戶所購商品的庫存信息门坷;
- 保存客戶付款信息宣鄙;
- 生成訂單并且保存到數(shù)據(jù)庫中袍镀;
- 更改用戶相關信息,例如購物數(shù)量等冻晤。
在正常情況下苇羡,這些操作都將順利進行,最終交易成功鼻弧,與交易相關的所有數(shù)據(jù)庫信息也成功地更新设江。但是锦茁,如果在執(zhí)行的途中遇到突然斷電或者其他意外情況,導致這一系列過程中任何一個環(huán)節(jié)出了差錯叉存,例如在更細商品庫存信息時發(fā)生異常码俩、顧客銀行賬戶余額不足等,都將導致整個交易過程失敗歼捏。而一旦失敗稿存,數(shù)據(jù)庫中所有信息都必須保持交易不影響數(shù)據(jù)庫的狀態(tài),即原有的庫存信息沒有被更新瞳秽、用戶也沒有付款瓣履、訂單也沒有生成。否則练俐,數(shù)據(jù)庫的信息將會不一致袖迎,或者出現(xiàn)更為嚴重的不可預測的后果。數(shù)據(jù)庫事務正是用來保證這種情況下交易的平穩(wěn)性和可預測性的技術腺晾。
事務的屬性
事務必須滿足四個屬性燕锥,即原子性(Atomicity)、一致性(Consistency)丘喻、隔離性(Isolation)脯宿、持久性(Durability),即ACID四種屬性泉粉。
(1)原子性
一個事務是一個不可分割的整體连霉,為了保證事務的總體目標,事務必須具有原子性嗡靡,即當數(shù)據(jù)修改時跺撼,要么全都執(zhí)行,要么全都不執(zhí)行讨彼。即歉井,不允許事務部分地完成,避免了只執(zhí)行這些操作的一部分而帶來的錯誤哈误。
(2)一致性
一個事務在執(zhí)行之前和執(zhí)行之后哩至,數(shù)據(jù)庫數(shù)據(jù)必須保持一致性。數(shù)據(jù)庫的一致性狀態(tài)應該滿足模式鎖指定的約束條件蜜自,那么在完整執(zhí)行該事務后菩貌,數(shù)據(jù)庫仍然處于一致性狀態(tài)。
例如:銀行轉賬重荠,轉賬前后兩個賬戶金額之和應保持不變箭阶。
(3)隔離性
由并發(fā)事務所作的修改必須與任何其它并發(fā)事務所作的修改隔離。事務查看數(shù)據(jù)庫時數(shù)據(jù)所處的狀態(tài),要么是另一并發(fā)事務修改它之前的狀態(tài)仇参,要么是另一事務修改它之后的狀態(tài)嘹叫,事務不會查看中間狀態(tài)的數(shù)據(jù)。
例如:對任何一對事務T1和T2诈乒,對T1而言罩扇,T2要么在T1開始之前已經(jīng)結束,要么在T1完成之后再開始執(zhí)行怕磨。
(4)持久性
也被稱為永久性暮蹂,事務完成以后,DBMS(數(shù)據(jù)庫管理系統(tǒng))保證它對數(shù)據(jù)庫中數(shù)據(jù)的修改是永久性的癌压,當系統(tǒng)或介質發(fā)生故障時仰泻,該修改也永久保持。持久性一般通過數(shù)據(jù)庫備份與恢復來保證滩届。
注意 嚴格而言集侯,數(shù)據(jù)庫事務屬性都是由數(shù)據(jù)庫管理系統(tǒng)來進行保證的,在整個應用程序的運行過程中帜消,應用程序無須去考慮數(shù)據(jù)庫的ACID實現(xiàn)棠枉。
一般情況下,通過執(zhí)行COMMIT(提交)或ROLLBACK(回滾)語句來終止事務泡挺。當執(zhí)行COMMIT語句時辈讶,自從事務啟動以來對數(shù)據(jù)庫所做的一切更改就成為永久性的,即被寫入到磁盤娄猫,而當執(zhí)行ROLLBACK語句時贱除,自從事務啟動以來對數(shù)據(jù)庫所做的一切更改都會被撤銷,并且數(shù)據(jù)庫中內容返回到事務開始之前所處的狀態(tài)媳溺。無論什么情況月幌,在事務完成時,都能保證回到一致性狀態(tài)悬蔽。
并發(fā)控制
(1)DBS(數(shù)據(jù)庫系統(tǒng))一個明顯的特點是多個用戶共享數(shù)據(jù)庫資源扯躺,尤其是多個用戶可以同時存取相同數(shù)據(jù)。
串行控制:如果事務是順序執(zhí)行的蝎困,即一個事務完成之后录语,再開始另一事務。
并行控制:如果DBMS可以同時接受多個事務禾乘,并且這些事務在時間上可以重疊執(zhí)行澎埠。
(2)并發(fā)控制概述
事務是并發(fā)控制的基本單位,保證事務ACID的特性是事務處理的重要任務盖袭,而并發(fā)操作有可能會破壞其ACID特性失暂。
DBMS并發(fā)控制機制的責任:對并發(fā)操作進行正確調度,保證事務的隔離更一般鳄虱,確保數(shù)據(jù)庫的一致性弟塞。
由于并發(fā)操作帶來的數(shù)據(jù)不一致性
如果沒有鎖定且多個用戶同時訪問一個數(shù)據(jù)庫,則當他們的事務同時使用相同的數(shù)據(jù)時可能會發(fā)生問題拙已。由于并發(fā)操作帶來的數(shù)據(jù)不一致性包括:丟失數(shù)據(jù)更新决记、讀“臟”數(shù)據(jù)(臟讀)、不可重復讀倍踪。
(1)更新丟失
兩個事務都同時更新一行數(shù)據(jù)系宫,一個事務對數(shù)據(jù)的更新把另一個事務對數(shù)據(jù)的更新覆蓋了。這是因為系統(tǒng)沒有執(zhí)行任何的鎖操作建车,因此并發(fā)并沒有被隔離開來扩借。
(2)臟讀
一個事務讀取到了另一事務未提交的數(shù)據(jù)操作結果。這是相當危險的缤至,因為很可能所有的操作都被回滾潮罪。
(3)不可重復讀
不可重復讀(Non-repeatable Reads):一個事務對同一行數(shù)據(jù)重復讀取兩次,但是卻得到了不同的結果领斥。包括以下情況:
虛讀:事務T1讀取某一數(shù)據(jù)后嫉到,事務T2對其做了修改,當事務T1再次讀取該數(shù)據(jù)時得到與前一次不同的值月洛。
幻讀:事務在操作過程中進行兩次查詢何恶,第二次查詢的結果包含了第一次查詢中未出現(xiàn)的數(shù)據(jù)或者缺少了第一次查詢中出現(xiàn)的數(shù)據(jù)。這是因為在兩次查詢過程中有另外一個事務插入數(shù)據(jù)造成的嚼黔。
事務隔離級別
為了避免上面出現(xiàn)的幾種情況细层,在標準SQL規(guī)范中,定義了4個事務隔離級別唬涧,不同的隔離級別對事務的處理不同今艺。
讀未提交(Read Uncommitted) | 只處理更新丟失。 | 如果一個事務已經(jīng)開始寫數(shù)據(jù)爵卒,則不允許其他事務同時進行寫操作虚缎,但允許其他事務讀此行數(shù)據(jù)。 | 可通過“排他寫鎖”實現(xiàn)钓株。 |
讀已提交(Read Committed) | 處理更新丟失实牡、臟讀。 | 讀取數(shù)據(jù)的事務允許其他事務繼續(xù)訪問改行數(shù)據(jù)轴合,但是未提交的寫事務將會禁止其他事務訪問改行创坞。 | 可通過“瞬間共享讀鎖”和“排他寫鎖”實現(xiàn)。 |
可重復讀仁芨稹(Repeatable Read) | 處理更新丟失题涨、臟讀和不可重復讀取偎谁。 | 讀取數(shù)據(jù)的事務將會禁止寫事務,但允許讀事務纲堵,寫事務則禁止任何其他事務巡雨。 | 可通過“共享讀鎖”和“排他寫鎖”實現(xiàn)。 |
序列化(Serializable) | 提供嚴格的事務隔離席函。 | 要求失去序列化執(zhí)行铐望,事務只能一個接一個地執(zhí)行,不能并發(fā)執(zhí)行茂附。 | 僅僅通過“行級鎖”是無法實現(xiàn)事務序列化的正蛙,必須通過其他機制保證新插入的數(shù)據(jù)不會被剛執(zhí)行查詢操作的事務訪問到。 |
隔離級別越高营曼,越能保證數(shù)據(jù)的完整性和統(tǒng)一性乒验,但是對并發(fā)性能的影響也越大。對于多數(shù)應用程序蒂阱,可以優(yōu)先考慮把數(shù)據(jù)庫系統(tǒng)的隔離級別設為Read Committed徊件。它能夠避免臟讀,而且具有較好的并發(fā)性能蒜危。盡管它會導致不可重復讀虱痕、幻讀和第二類丟失更新這些并發(fā)問題,在可能出現(xiàn)這類問題的個別場合辐赞,可以由應用程序采用悲觀鎖或樂觀鎖來控制部翘。
原文鏈接:https://blog.csdn.net/weixin_39651041/java/article/details/79980202
MySQl
MySql默認的隔離級別為Repeatable Read,因此只會出現(xiàn)幻讀的情況响委。
幻讀
事務在插入已經(jīng)檢查過不存在的記錄時新思,驚奇的發(fā)現(xiàn)這些數(shù)據(jù)已經(jīng)存在了,之前的檢測獲取到的數(shù)據(jù)如同鬼影一般赘风。
例子:
在事務1中夹囚,查詢User表id為1的是用戶否存在,如果不存在則插入一條id為1的數(shù)據(jù)邀窃。
select * from User where id = 1;
在事務1查詢結束后荸哟,事務2往User表中插入了一條id為1的數(shù)據(jù)。
insert into
User(
id,
name) values (1, 'Joonwhee');
此時瞬捕,由于事務1查詢到id為1的用戶不存在鞍历,因此插入1條id為1的數(shù)據(jù)。
insert into ` User`(`id`, `name`) values (1, 'Chillax');
但是由于事務2已經(jīng)插入了1條id為1的數(shù)據(jù)肪虎,因此此時會報主鍵沖突劣砍,對于事務1 的業(yè)務來說是執(zhí)行失敗的,這里事務1 就是發(fā)生了幻讀扇救,因為事務1讀取的數(shù)據(jù)狀態(tài)并不能支持他的下一步的業(yè)務刑枝,見鬼了一樣香嗓。這里要靈活的理解讀取的意思,第一次select是讀取装畅,第二次的insert其實也屬于隱式的讀取靠娱,只不過是在mysql的機制中讀取的,插入數(shù)據(jù)也是要先讀取一下有沒有主鍵沖突才能決定是否執(zhí)行插入洁灵。
Oracle
Oracle默認的隔離級別為Read Committed,因此可能出現(xiàn)不可重復度和幻讀掺出。
不可重復讀
同樣的條件徽千,你讀取過的數(shù)據(jù),再次讀取出來發(fā)現(xiàn)值不一樣了汤锨。
例子:
在事務1中双抽,JoonWhee讀取了自己的工資為1000,但是此時事務1的操作還并沒有完成 闲礼,后面還有1次相同的讀取操作牍汹。
con1 = getConnection();
select salary from employee where employeeName="JoonWhee";
在事務2中,這時財務人員修改了JoonWhee的工資為2000柬泽,并提交了事務慎菲。
con2 = getConnection(); update employee set salary = 2000 where employeeName = "JoonWhee"; con2.commit();
在事務1中,JoonWhee再次讀取自己的工資時锨并,工資變?yōu)榱?000 露该。
select salary from employee where employeeName ="JoonWhee";
在一個事務中前后兩次讀取的結果并不一致,導致了不可重復讀第煮。
幻讀
同樣的條件解幼,第1次和第2次讀出來的記錄數(shù)不一樣。
例子:
目前工資為1000的員工有10人包警。
事務1撵摆,讀取所有工資為1000的員工,共讀取10條記錄 害晦。
con1 = getConnection();
Select * from employee where salary =1000;
這時另一個事務向employee表插入了一條員工記錄特铝,工資也為1000
con2 = getConnection(); Insert into employee(employeeName,salary) values("Lili",1000); con2.commit();
事務1再次讀取所有工資為1000的員工,共讀取到了11條記錄壹瘟,這就產(chǎn)生了幻讀苟呐。
select * from employee where salary =1000;
原文鏈接:https://blog.csdn.net/v123411739/java/article/details/39298127
擴展閱讀: