
本篇文章主要介紹數(shù)據(jù)庫的四大特性ACID,以及說明一下數(shù)據(jù)庫的隔離級(jí)別。
如果想要說明一個(gè)數(shù)據(jù)庫或者一個(gè)框架支持事務(wù)性操作,則必須要滿足下面的四大特性
1. 原子性(Atomicity)
原子性是指事務(wù)包含的所有操作要么全部成功,要么全部失敗回滾。失敗回滾的操作事務(wù),將不能對(duì)事物有任何影響。
2. 一致性(Consistency)
一致性是指事務(wù)必須使數(shù)據(jù)庫從一個(gè)一致性狀態(tài)變換到另一個(gè)一致性狀態(tài),也就是說一個(gè)事務(wù)執(zhí)行之前和執(zhí)行之后都必須處于一致性狀態(tài)。
數(shù)據(jù)庫狀態(tài)如何變化?每一次數(shù)據(jù)變更就會(huì)導(dǎo)致數(shù)據(jù)庫的狀態(tài)遷移。如果數(shù)據(jù)庫的初始狀態(tài)是C0,第一次事務(wù)T1的提交就會(huì)導(dǎo)致系統(tǒng)生成一個(gè)SYSTEM CHANGE NUMBER(SCN),這是數(shù)據(jù)庫狀態(tài)從C0轉(zhuǎn)變成C1。執(zhí)行第二個(gè)事務(wù)T2的時(shí)候數(shù)據(jù)庫狀態(tài)從T1變成T2,以此類推,執(zhí)行第Tn次事務(wù)的時(shí)候數(shù)據(jù)庫狀態(tài)由C(n-1)變成Cn。
一致性可以從一致讀和一致寫兩個(gè)方面來理解。
- 一致讀 事務(wù)讀取數(shù)據(jù)只能從一個(gè)狀態(tài)中讀取,不能從2個(gè)或者2個(gè)以上狀態(tài)讀取。也就是T(n)只能從C(n-1),C(n-2)... C(1)中的一個(gè)狀態(tài)讀取數(shù)據(jù),不能一部分?jǐn)?shù)據(jù)讀取自C(n-1),而另一部分?jǐn)?shù)據(jù)讀取自C(n-2)。
- 一致寫 事務(wù)執(zhí)行的數(shù)據(jù)變更只能基于上一個(gè)一致的狀態(tài),且只能體現(xiàn)在一個(gè)狀態(tài)中。T(n)的變更結(jié)果只能基于C(n-1),C(n-2), ...C(1)狀態(tài),且只能體現(xiàn)在C(n)狀態(tài)中。也就是說,一個(gè)狀態(tài)只能有一個(gè)事務(wù)變更數(shù)據(jù),不允許有2個(gè)或者2個(gè)以上事務(wù)在一個(gè)狀態(tài)中變更數(shù)據(jù)。至于具體一致寫基于哪個(gè)狀態(tài),需要判斷T(n)事務(wù)是否和T(n-1),T(n-2),...T(1)有依賴關(guān)系。
3. 隔離性(Isolation)
隔離性是指當(dāng)多個(gè)用戶并發(fā)訪問數(shù)據(jù)庫時(shí),比如同時(shí)訪問一張表,數(shù)據(jù)庫每一個(gè)用戶開啟的事務(wù),不能被其他事務(wù)所做的操作干擾,多個(gè)并發(fā)事務(wù)之間,應(yīng)當(dāng)相互隔離。
例如同時(shí)有T1和T2兩個(gè)并發(fā)事務(wù),從T1角度來看,T2要不在T1執(zhí)行之前就已經(jīng)結(jié)束,要么在T1執(zhí)行完成后才開始。將多個(gè)事務(wù)隔離開,每個(gè)事務(wù)都不能訪問到其他事務(wù)操作過程中的狀態(tài)。
關(guān)于事務(wù)的隔離性,數(shù)據(jù)庫也提供了多種方案,后面我們將會(huì)進(jìn)行詳細(xì)介紹
4. 持久性(Durability)
持久性是指事務(wù)的操作,一旦提交,對(duì)于數(shù)據(jù)庫中數(shù)據(jù)的改變是永久性的,即使數(shù)據(jù)庫發(fā)生故障也不能丟失已提交事務(wù)所完成的改變。
在了解完數(shù)據(jù)庫的四大特性之后,我們來討論一下數(shù)據(jù)庫的隔離級(jí)別的問題。在此之前,我們考慮在沒有數(shù)據(jù)庫隔離性的情況下,多用戶并發(fā)操作可能會(huì)發(fā)生的問題。
1. 臟讀
臟讀是指一個(gè)事務(wù)讀取了未提交事務(wù)執(zhí)行過程中的數(shù)據(jù)。
當(dāng)一個(gè)事務(wù)的操作正在多次修改數(shù)據(jù),而在事務(wù)還未提交的時(shí)候,另外一個(gè)并發(fā)事務(wù)來讀取了數(shù)據(jù),就會(huì)導(dǎo)致讀取到的數(shù)據(jù)并非是最終持久化之后的數(shù)據(jù),這個(gè)數(shù)據(jù)就是臟讀的數(shù)據(jù)。
最典型的例子就是銀行轉(zhuǎn)賬,從A賬戶轉(zhuǎn)賬100到B賬戶,腳本命令為
update account set money = money + 100 where username = 'B';
update account set money = money - 100 where username = 'A';
在這個(gè)事務(wù)執(zhí)行過程中,另外一個(gè)事務(wù)讀取結(jié)果發(fā)現(xiàn)B賬戶中的錢已經(jīng)到賬,提示B錢已到賬,B就進(jìn)行了下一步的操作。但是最終轉(zhuǎn)賬事務(wù)失敗,導(dǎo)致操作回滾。實(shí)際上B并未收到錢,但是進(jìn)行了下一步的操作,造成了損失,這就是臟讀。
2. 不可重復(fù)讀
不可重復(fù)讀是指對(duì)于數(shù)據(jù)庫中的某個(gè)數(shù)據(jù),一個(gè)事務(wù)執(zhí)行過程中多次查詢返回不同查詢結(jié)果,這就是在事務(wù)執(zhí)行過程中,數(shù)據(jù)被其他事務(wù)提交修改了。
不可重復(fù)讀同臟讀的區(qū)別在于,臟讀是一個(gè)事務(wù)讀取了另一未完成的事務(wù)執(zhí)行過程中的數(shù)據(jù),而不可重復(fù)讀是一個(gè)事務(wù)執(zhí)行過程中,另一事務(wù)提交并修改了當(dāng)前事務(wù)正在讀取的數(shù)據(jù)。
3. 虛讀(幻讀)
幻讀是事務(wù)非獨(dú)立執(zhí)行時(shí)發(fā)生的一種現(xiàn)象,例如事務(wù)T1批量對(duì)一個(gè)表中某一列列值為1的數(shù)據(jù)修改為2的變更,但是在這時(shí),事務(wù)T2對(duì)這張表插入了一條列值為1的數(shù)據(jù),并完成提交。此時(shí),如果事務(wù)T1查看剛剛完成操作的數(shù)據(jù),發(fā)現(xiàn)還有一條列值為1的數(shù)據(jù)沒有進(jìn)行修改,而這條數(shù)據(jù)其實(shí)是T2剛剛提交插入的,這就是幻讀。
幻讀和不可重復(fù)讀都是讀取了另一條已經(jīng)提交的事務(wù)(這點(diǎn)同臟讀不同),所不同的是不可重復(fù)讀查詢的都是同一個(gè)數(shù)據(jù)項(xiàng),而幻讀針對(duì)的是一批數(shù)據(jù)整體(比如數(shù)據(jù)的個(gè)數(shù))。
隔離級(jí)別說明
MySQL定義了四種隔離級(jí)別,包括一些具體規(guī)則,用于限定事務(wù)內(nèi)外哪些改變是可見的,哪些改變是不可見的。低級(jí)別的隔離一般支持更高的并發(fā)處理,并且擁有更低的系統(tǒng)開銷。
REPEATABLE READ Repeatable Read 可重復(fù)讀
MySQL數(shù)據(jù)庫默認(rèn)的隔離級(jí)別。該級(jí)別解決了READ UNCOMMITTED隔離級(jí)別導(dǎo)致的問題。它保證同一事務(wù)的多個(gè)實(shí)例在并發(fā)讀取事務(wù)時(shí),會(huì)“看到同樣的”數(shù)據(jù)行。不過,這會(huì)導(dǎo)致另外一個(gè)棘手問題“幻讀”。InnoDB和Falcon存儲(chǔ)引擎通過多版本并發(fā)控制機(jī)制解決了幻讀問題。
READ COMMITTED Read Committed 讀取提交內(nèi)容
大多數(shù)數(shù)據(jù)庫系統(tǒng)的默認(rèn)隔離級(jí)別(但是不是MySQL的默認(rèn)隔離級(jí)別),滿足了隔離的早先簡單定義:一個(gè)事務(wù)開始時(shí),只能“看見”已經(jīng)提交事務(wù)所做的改變,一個(gè)事務(wù)從開始到提交前,所做的任何數(shù)據(jù)改變都是不可見的,除非已經(jīng)提交。這種隔離級(jí)別也支持所謂的“不可重復(fù)讀”。這意味著用戶運(yùn)行同一個(gè)語句兩次,看到的結(jié)果是不同的。
READ UNCOMMITTED Read UnCommitted 讀取未提交內(nèi)容
在這個(gè)隔離級(jí)別,所有事務(wù)都可以“看到”未提交事務(wù)的執(zhí)行結(jié)果。在這種級(jí)別上,可能會(huì)產(chǎn)生很多問題,除非用戶真的知道自己在做什么,并有很好的理由選擇這樣做。本隔離級(jí)別很少用于實(shí)際應(yīng)用,因?yàn)樗男阅芤膊槐仄渌阅芎枚嗌伲鴦e的級(jí)別還有其他更多的優(yōu)點(diǎn)。讀取未提交數(shù)據(jù),也被稱為“臟讀”
SERIALIZABLE Serializable 可串行化
該級(jí)別是最高級(jí)別的隔離級(jí)。它通過強(qiáng)制事務(wù)排序,使之不可能相互沖突,從而解決幻讀問題。簡而言之,SERIALIZABLE是在每個(gè)讀的數(shù)據(jù)行上加鎖。在這個(gè)級(jí)別,可能導(dǎo)致大量的超時(shí)Timeout和鎖競爭Lock Contention現(xiàn)象,實(shí)際應(yīng)用中很少使用到這個(gè)級(jí)別,但如果用戶的應(yīng)用為了數(shù)據(jù)的穩(wěn)定性,需要強(qiáng)制減少并發(fā)的話,也可以選擇這種隔離級(jí)
下面的表格總結(jié)了各種隔離級(jí)別和各自的缺點(diǎn)
| 隔離級(jí)別 | 臟讀可能性 | 不可重復(fù)讀可能性 | 幻讀可能性 | 加鎖讀 |
|---|---|---|---|---|
| READ UNCOMMITTED | 是 | 是 | 是 | 否 |
| READ COMMITTED | 否 | 是 | 是 | 否 |
| REPEATABLE READ | 否 | 否 | 是 | 否 |
| SERIALIZABLE | 否 | 否 | 否 | 是 |
修改隔離級(jí)別的方法
全局修改
全局修改需要修改MySql的全局文件mysql.ini,修改內(nèi)容如下
1 #可選參數(shù)有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE.
2 [mysqld]
3 transaction-isolation = REPEATABLE-READ
語句修改
在命令行模式下連上MySql后,可以使用下列語句查看MySql當(dāng)前隔離級(jí)別
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
可以使用下面的命令修改當(dāng)前會(huì)話Session的隔離級(jí)
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set (0.00 sec)
AutoCommit 事務(wù)自動(dòng)提交
MySql中有AutoCommit參數(shù),默認(rèn)為on,也就是開啟狀態(tài)。它的作用是每一條單獨(dú)的查詢都是一個(gè)事務(wù),自動(dòng)開始,自動(dòng)提交(語句執(zhí)行完成就提交。如果你要適用select for update,而不手動(dòng)調(diào)用 start transaction,這個(gè)for update的行鎖機(jī)制等于沒用,因?yàn)樾墟i在自動(dòng)提交后就釋放了)。所以事務(wù)隔離級(jí)別和鎖機(jī)制即使你不顯式調(diào)用start transaction,這種機(jī)制在單獨(dú)一條語句查詢中也是適用的。
在命令行模式下可以使用下面的命令查看當(dāng)前MySql的autocommit是否開啟
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)
如果需要關(guān)閉autocommit,我們可以使用下面語句設(shè)置
mysql> set autocommit=0;
0就是OFF,1就是ON。設(shè)置為OFF之后,則用戶執(zhí)行語句之后,將一直處于一個(gè)事務(wù)中,直到執(zhí)行commit或者rollback,才會(huì)結(jié)束當(dāng)前事務(wù),重新開始新的事務(wù)。
鎖機(jī)制
共享鎖
由讀表操作加上的鎖,加鎖后其他用戶只能獲取該表或行的共享鎖,不能獲取排它鎖,也就是說只能讀不能寫
排它鎖
由寫表操作加上的鎖,加鎖后其他用戶不能獲取該表或行的任何鎖,典型是mysql事務(wù)
根據(jù)鎖的范圍,可以分為
表鎖
給整張表加鎖
行鎖
給行數(shù)據(jù)加鎖
因此鎖可以分為表級(jí)共享鎖、行級(jí)共享鎖、表級(jí)排它鎖、行級(jí)排它鎖。
參考: