1、事務的四大特性(ACID)
#### 1.1、原子性(Atomicity) 原子性是指事務包含的一系列操作要么全部成功,要么全部回滾,不存在部分成功或者部分回滾,是一個不可分割的操作整體。1.2、一致性(Consistency)
一致性是可以理解為事務對數據完整性約束的遵循,這些約束可能包括主鍵約束、唯一索引約束、外鍵約束等等。事務執行前后,數據都是合法的狀態,不會違背任何的數據完整性
就拿轉賬來說,A和B加起來有5000塊錢,不管A和B如何轉賬,轉幾次賬,A和B加起來的錢永遠都是5000塊。
總之,可以理解為:一致性是為了保證數據的完整性。
1.3、隔離性(Isolation)
隔離性是指當多個用戶並發操作數據庫,比如操作同一張表,數據庫為每一個用戶開啟的事務,不能被其他的事務所干擾或者影響,事務之間是彼此獨立的。
1.4、永久性(Durability)
永久性是指一個事務一旦提交了,那么對數據庫中數據的改變就是永久的,即使是在數據庫發生故障時,也不會丟失事務提交的數據。
2、事務的隔離性
事務的隔離性。當多個線程開啟事務操作數據庫中的數據時,數據庫要能進行隔離操作,以保證各個線程獲取數據的准確性; 如果不考慮事務的隔離性,會發生以下幾個問題;2.1、臟讀
臟讀是指一個事務在處理過程中讀取了另一個事務未提交的數據。比如,A向B轉賬
update account set money = money + 100 where name = 'B';
update account set money = money - 100 where name = 'A'
當只執行第一條SQL時,A通知B查看賬戶,B發現確實錢已到賬(此時即發生了臟讀),
而之后無論第二條SQL是否執行,只要該事務不提交,則所有操作都將回滾,那么當B以后再次查看賬戶時就會發現錢其實並沒有轉
2.2、不可重復讀
- 不可重復讀是指在對於數據庫中的某個數據,一個事務范圍內多次查詢卻返回了不同的數據值,這是由於在查詢間隔,被另一個事務修改並提交了。
- 例如事務T1在讀取某一數據,而事務T2立馬修改了這個數據並且提交事務給數據庫,事務T1再次讀取該數據就得到了不同的結果,發生了不可重復讀。
- 不可重復讀和臟讀的區別是,臟讀是某一事務讀取了另一個事務未提交的臟數據,而不可重復讀則是讀取了前一事務提交的數據。
- 在某些情況下,不可重復讀並不是問題,比如我們多次查詢某個數據當然以最后查詢得到的結果為主。但在另一些情況下就有可能發生問題,例如對於同一個數據A和B依次查詢就可能不同,A和B就可能打起來了……
2.3、幻讀
幻讀是事務非獨立執行時發生的一種現象。
例如事務T1對一個表中所有的行的某個數據項做了從“1”修改為“2”的操作,這時事務T2又對這個表中插入了一行數據項,而這個數據項的數值還是為“1”並且提交給數據庫。而操作事務T1的用戶如果再查看剛剛修改的數據,會發現還有一行沒有修改,其實這行是從事務T2中添加的,就好像產生幻覺一樣,這就是發生了幻讀。
幻讀和不可重復讀都是讀取了另一條已經提交的事務(這點就臟讀不同),所不同的是不可重復讀查詢的都是同一個數據項,而幻讀針對的是一批數據整體(比如數據的個數)
3、MySQL的隔離級別
> * Serializable (串行化):可避免臟讀、不可重復讀、幻讀的發生 > * Repeatable read (可重復讀):可避免臟讀、不可重復讀的發生 > * Read committed (讀已提交):可避免臟讀的發生 > * Read uncommitted (讀未提交):最低級別,任何情況都無法保證以上四種隔離級別最高的是Serializable級別,最低的是Read uncommitted級別,當然級別越高,執行效率就越低。
像Serializable這樣的級別,就是以鎖表的方式(類似於Java多線程中的鎖)使得其他的線程只能在鎖外等待)
所以平時選用何種隔離級別應該根據實際情況。
3.1、MySQL和Oracle隔離級別的對比
MySQL支持以上4種隔離級別,默認的隔離級別是Repeatable read (可重復讀)
Oracle只支持Serializable (串行化)級別和Read committed (讀已提交)這兩種級別,默認的隔離級別是Read committed (讀已提交)
查看mysql事務的隔離級別
select @@tx_isolation
4、舉例說明事務的隔離級別
#### 4.1、Repeatable read (可重復讀)開啟兩個事務A、B,分別對同一個表中的數據進行操作,操作流程如下:
結果:在Repeatable read的隔離級別下,T4得到的結果仍然是Tom:
此時,如果將事務A提交之后,再查詢,得到的將是最新的結果:
4.2、Read committed (讀已提交)
現在看看將隔離級別設置為讀已提交,返回的結果又是什么
set session transaction isolation level read committed
注意:此種修改只對當前會話有效,如果要全局修改隔離級別,需要到mysql安裝目錄下的my.ini最后添加
transaction-isolation = REPEATABLE-READ
結果:在Read committed的隔離級別下,T4得到的結果就是最新,說明讀到了已提交的: