數(shù)據(jù)庫四大特性及數(shù)據(jù)庫隔離級(jí)別

MySql
MySql

本篇文章主要介紹數(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í)排它鎖

參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容