事務(wù)
一、概述
1.什么是事務(wù)
事務(wù)指邏輯上的一組操作,組成這組操作的各個單元,要么全部成功,要么全部不成功。
- 一個最小的不可再分的工作單元
- 通常一個事務(wù)對應(yīng)了一個完整的業(yè)務(wù)
- 而一個完整的業(yè)務(wù)需要批量的DML語句共同聯(lián)合完成
- 事務(wù)只和DML語句有關(guān)系,或者說DML語句才有事務(wù)
例如:A——B轉(zhuǎn)帳,對應(yīng)于如下兩條sql語句
update account set money=money-100 where name=‘a(chǎn)’;
update account set money=money+100 where name=‘b’;
以上的兩條DML語句要求必須同時成功或者同時失敗,最小單元,不可再分。當(dāng)?shù)谝粭lDML語句執(zhí)行成功之后,并不能將底層數(shù)據(jù)庫中第一個賬戶的數(shù)據(jù)修改,只是將操作記錄了,這個記錄是在內(nèi)存中完成的,當(dāng)?shù)诙lDML語句執(zhí)行成功之后,和底層數(shù)據(jù)庫文件中的數(shù)據(jù)完成同步。若第二條DML語句執(zhí)行失敗,清空所有的歷史操作記錄。要完成以上的功能,必須借助事務(wù)。
2.事務(wù)的四個特征(ACID)
- 原子性(Atomicity):
原子性是指事務(wù)是一個不可分割的工作單位,事務(wù)中的操作要么都發(fā)生,要么都不發(fā)生。 - 一致性(Consistency):事務(wù)必須使數(shù)據(jù)庫從一個一致性狀態(tài)變換到另外一個一致性狀態(tài)。
- 隔離性(Isolation):事務(wù)的隔離性是多個用戶并發(fā)訪問數(shù)據(jù)庫時,數(shù)據(jù)庫為每一個用戶開啟的事務(wù),不能被其他事務(wù)的操作數(shù)據(jù)所干擾,多個并發(fā)事務(wù)之間要相互隔離。
- 持久性(Durability):持久性是指一個事務(wù)一旦被提交,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就是永久性的,接下來即使數(shù)據(jù)庫發(fā)生故障也不應(yīng)該對其有任何影響。
3.關(guān)于一些術(shù)語:
- 開啟事務(wù):start transaction
- 結(jié)束事務(wù):end transaction
- 提交事務(wù):commit transaction
- 回滾事務(wù):rollback transaction
二、事務(wù)的提交與回滾
和事務(wù)有關(guān)的兩條SQL語句(TCL):
commit; 提交
rollback; 回滾
事務(wù)開啟的標(biāo)志:
任何一條DML語句(insert,update,delete)執(zhí)行,標(biāo)志事務(wù)的開啟
事務(wù)結(jié)束的標(biāo)志:
提交或回滾
提交:成功的結(jié)束,將所有的DML語句操作歷史記錄和底層硬盤文件中的數(shù)據(jù)來一次同步。
回滾:失敗的時候,將所有的DML語句操作歷史全部清空
注意:在事務(wù)進(jìn)行過程中,未結(jié)束之前,DML語句不會更改底層數(shù)據(jù)庫文件中的數(shù)據(jù),只是將歷史操作記錄下來,在內(nèi)存中完成記錄。只有在事務(wù)結(jié)束的時候,而且是成功的結(jié)束的時候才會修改底層硬盤文件中的數(shù)據(jù)。
三、自動提交模式
演示提交與回滾:
在mysql數(shù)據(jù)庫管理系統(tǒng)中,默認(rèn)情況下,事務(wù)是自動提交的
即,執(zhí)行一條DML語句,開啟事務(wù),并提交事務(wù)。
自動提交機(jī)制可以關(guān)閉:
方式一:
start transaction; //手動開啟事務(wù)
DML語句...
commit; //手動提交事務(wù)(事務(wù)成功地結(jié)束)
start transaction; //手動開啟事務(wù)
DML語句...
rollback; //手動回滾事務(wù)(事務(wù)失敗地結(jié)束)
方式二:
關(guān)閉自動提交:
set autocommit=off;
或:set session autocommit=off;
打開自動提交
set autocommit=on;
或:set session autocommit=on;
以上打開和關(guān)閉自動提交機(jī)制,只對當(dāng)前會話有效
四、事務(wù)的隔離級別
多個線程開啟各自事務(wù)操作數(shù)據(jù)庫中數(shù)據(jù)時,數(shù)據(jù)庫系統(tǒng)要負(fù)責(zé)隔離操作,以保證各個線程在獲取數(shù)據(jù)時的準(zhǔn)確性。
如果不考慮隔離性,可能會引發(fā)如下問題:
1.臟讀:指一個事務(wù)讀取了另外一個事務(wù)未提交的數(shù)據(jù)。這是非常危險的
假設(shè)A向B轉(zhuǎn)帳100元,對應(yīng)sql語句如下所示:
1.update account set money=money-100 while name=‘a(chǎn)’;
2.update account set money=money+100 while name=‘b’;
當(dāng)?shù)?條執(zhí)行完,第2條還沒執(zhí)行(A未提交時),
如果此時B查詢自己的帳戶,就會發(fā)現(xiàn)自己多了100元錢。
如果A等B走后再回滾,B就會損失100元。
2.不可重復(fù)讀(針對一條記錄的,同一條記錄前后不一樣)
在一個事務(wù)內(nèi)讀取表中的某一行數(shù)據(jù),多次讀取結(jié)果不同。
例如銀行想查詢A帳戶余額,第一次查詢A帳戶為200元,此時A向帳戶內(nèi)存了100元并提交了,銀行接著又進(jìn)行了一次查詢,此時A帳戶為300元了。銀行兩次查詢不一致,可能就會很困惑,不知道哪次查詢是準(zhǔn)的。
和臟讀的區(qū)別是,臟讀是讀取前一事務(wù)未提交的臟數(shù)據(jù),不可重復(fù)讀是重新讀取了前一事務(wù)已提交的數(shù)據(jù)。
很多人認(rèn)為這種情況就對了,無須困惑,當(dāng)然是后面的為準(zhǔn)。我們可以考慮這樣一種情況,比如銀行程序需要將查詢結(jié)果分別輸出到電腦屏幕和寫到文件中,結(jié)果在一個事務(wù)中針對輸出的目的地,進(jìn)行的兩次查詢不一致,導(dǎo)致文件和屏幕中的結(jié)果不一致,銀行工作人員就不知道以哪個為準(zhǔn)了。
3.虛讀(幻讀,同一張表前后不一樣記錄數(shù))
是指在一個事務(wù)內(nèi)讀取到了別的事務(wù)插入的數(shù)據(jù),導(dǎo)致前后讀取不一致。
如丙存款100元未提交,這時銀行做報表統(tǒng)計account表中所有用戶的總額為500元,然后丙提交了,這時銀行再統(tǒng)計發(fā)現(xiàn)帳戶為600元了,造成虛讀同樣會使銀行不知所措,到底以哪個為準(zhǔn)。
數(shù)據(jù)庫共定義了四種隔離級別:
Serializable(串行化):可避免臟讀、不可重復(fù)讀、虛讀情況的發(fā)生。
Repeatable read(可重復(fù)讀):可避免臟讀、不可重復(fù)讀情況的發(fā)生。
Read committed(讀已提交):可避免臟讀情況發(fā)生。
Read uncommitted(讀未提交):最低級別,以上情況均無法保證。
- mysql數(shù)據(jù)庫管理系統(tǒng)默認(rèn)的隔離級別:repeatable read
- oracle數(shù)據(jù)庫管理系統(tǒng)默認(rèn)的隔離級別:read committed
隔離級別的設(shè)置
1.通過修改配置文件設(shè)置
在my.ini文件中使用transaction-isolation選項來設(shè)置服務(wù)器的缺省隔離界別,該選項值可以是:
- READ-UNCOMMITTED
- READ-COMMITTED
- REPEATABLE-READ
- SERILIZABLE
例:
[mysqld]
transaction-isolation=READ-COMMITTED
2.通過命令動態(tài)設(shè)置
命令格式:
set [無/session/global] transaction isolation level <isolation-level>;
<isolation-level>可以是:
- READ-UNCOMMITTED
- READ-COMMITTED
- REPEATABLE-READ
- SERILIZABLE
隔離級別的作用范圍分兩種:
- 全局級:對所有的會話有效(global)
- 會話級:只對當(dāng)前的會話有效(session或不寫)
查看隔離級別:
查看當(dāng)前會話的隔離級別
select @@tx_isolation;
select @@session.tx_isolation;
查看全局的事務(wù)隔離級別
select @@global.tx_isolation;