資料庫四大特性及資料庫隔離級別
本篇文章主要介紹資料庫的四大特性ACID,以及說明一下資料庫的隔離級別。
如果想要說明一個數據庫或者一個框架支援事務性操作,則必須要滿足下面的四大特性
1. 原子性(Atomicity)
原子性是指事務包含的所有操作要麼全部成功,要麼全部失敗回滾。失敗回滾的操作事務,將不能對事物有任何影響。
2. 一致性(Consistency)
一致性是指事務必須使資料庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之後都必須處於一致性狀態。
資料庫狀態如何變化?每一次資料變更就會導致資料庫的狀態遷移。如果資料庫的初始狀態是C0,第一次事務T1的提交就會導致系統生成一個SYSTEM CHANGE NUMBER(SCN),這是資料庫狀態從C0轉變成C1。執行第二個事務T2的時候資料庫狀態從T1變成T2,以此類推,執行第Tn次事務的時候資料庫狀態由C(n-1)變成Cn。
一致性可以從一致讀和一致寫兩個方面來理解。
- 一致讀 事務讀取資料只能從一個狀態中讀取,不能從2個或者2個以上狀態讀取。也就是T(n)只能從C(n-1),C(n-2)... C(1)中的一個狀態讀取資料,不能一部分資料讀取自C(n-1),而另一部分資料讀取自C(n-2)。
- 一致寫 事務執行的資料變更只能基於上一個一致的狀態,且只能體現在一個狀態中。T(n)的變更結果只能基於C(n-1),C(n-2), ...C(1)狀態,且只能體現在C(n)狀態中。也就是說,一個狀態只能有一個事務變更資料,不允許有2個或者2個以上事務在一個狀態中變更資料。至於具體一致寫基於哪個狀態,需要判斷T(n)事務是否和T(n-1),T(n-2),...T(1)有依賴關係。
3. 隔離性(Isolation)
隔離性是指當多個使用者併發訪問資料庫時,比如同時訪問一張表,資料庫每一個使用者開啟的事務,不能被其他事務所做的操作干擾,多個併發事務之間,應當相互隔離。
例如同時有T1和T2兩個併發事務,從T1角度來看,T2要不在T1執行之前就已經結束,要麼在T1執行完成後才開始。將多個事務隔離開,每個事務都不能訪問到其他事務操作過程中的狀態。
關於事務的隔離性,資料庫也提供了多種方案,後面我們將會進行詳細介紹
4. 永續性(Durability)
永續性是指事務的操作,一旦提交,對於資料庫中資料的改變是永久性的,即使資料庫發生故障也不能丟失已提交事務所完成的改變。
在瞭解完資料庫的四大特性之後,我們來討論一下資料庫的隔離級別的問題。在此之前,我們考慮在沒有資料庫隔離性的情況下,多使用者併發操作可能會發生的問題。
1. 髒讀
髒讀是指一個事務讀取了未提交事務執行過程中的資料。
當一個事務的操作正在多次修改資料,而在事務還未提交的時候,另外一個併發事務來讀取了資料,就會導致讀取到的資料並非是最終持久化之後的資料,這個資料就是髒讀的資料。
最典型的例子就是銀行轉賬,從A賬戶轉賬100到B賬戶,指令碼命令為
update account set money = money + 100 where username = 'B';
update account set money = money - 100 where username = 'A';
在這個事務執行過程中,另外一個事務讀取結果發現B賬戶中的錢已經到賬,提示B錢已到賬,B就進行了下一步的操作。但是最終轉賬事務失敗,導致操作回滾。實際上B並未收到錢,但是進行了下一步的操作,造成了損失,這就是髒讀。
2. 不可重複讀
不可重複讀是指對於資料庫中的某個資料,一個事務執行過程中多次查詢返回不同查詢結果,這就是在事務執行過程中,資料被其他事務提交修改了。
不可重複讀同髒讀的區別在於,髒讀是一個事務讀取了另一未完成的事務執行過程中的資料,而不可重複讀是一個事務執行過程中,另一事務提交併修改了當前事務正在讀取的資料。
3. 虛讀(幻讀)
幻讀是事務非獨立執行時發生的一種現象,例如事務T1批量對一個表中某一列列值為1的資料修改為2的變更,但是在這時,事務T2對這張表插入了一條列值為1的資料,並完成提交。此時,如果事務T1檢視剛剛完成操作的資料,發現還有一條列值為1的資料沒有進行修改,而這條資料其實是T2剛剛提交插入的,這就是幻讀。
幻讀和不可重複讀都是讀取了另一條已經提交的事務(這點同髒讀不同),所不同的是不可重複讀查詢的都是同一個資料項,而幻讀針對的是一批資料整體(比如資料的個數)。
隔離級別說明
MySQL定義了四種隔離級別,包括一些具體規則,用於限定事務內外哪些改變是可見的,哪些改變是不可見的。低級別的隔離一般支援更高的併發處理,並且擁有更低的系統開銷。
REPEATABLE READ Repeatable Read
可重複讀
MySQL資料庫預設的隔離級別。該級別解決了READ UNCOMMITTED隔離級別導致的問題。它保證同一事務的多個例項在併發讀取事務時,會“看到同樣的”資料行。不過,這會導致另外一個棘手問題“幻讀”。InnoDB和Falcon儲存引擎通過多版本併發控制機制解決了幻讀問題。
READ COMMITTED Read Committed
讀取提交內容
大多數資料庫系統的預設隔離級別(但是不是MySQL的預設隔離級別),滿足了隔離的早先簡單定義:一個事務開始時,只能“看見”已經提交事務所做的改變,一個事務從開始到提交前,所做的任何資料改變都是不可見的,除非已經提交。這種隔離級別也支援所謂的“不可重複讀”。這意味著使用者運行同一個語句兩次,看到的結果是不同的。
READ UNCOMMITTED Read UnCommitted
讀取未提交內容
在這個隔離級別,所有事務都可以“看到”未提交事務的執行結果。在這種級別上,可能會產生很多問題,除非使用者真的知道自己在做什麼,並有很好的理由選擇這樣做。本隔離級別很少用於實際應用,因為它的效能也不必其他效能好多少,而別的級別還有其他更多的優點。讀取未提交資料,也被稱為“髒讀”
SERIALIZABLE Serializable
可序列化
該級別是最高級別的隔離級。它通過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。簡而言之,SERIALIZABLE是在每個讀的資料行上加鎖。在這個級別,可能導致大量的超時Timeout
和鎖競爭Lock Contention
現象,實際應用中很少使用到這個級別,但如果使用者的應用為了資料的穩定性,需要強制減少併發的話,也可以選擇這種隔離級
下面的表格總結了各種隔離級別和各自的缺點
隔離級別 | 髒讀可能性 | 不可重複讀可能性 | 幻讀可能性 | 加鎖讀 |
---|---|---|---|---|
READ UNCOMMITTED | 是 | 是 | 是 | 否 |
READ COMMITTED | 否 | 是 | 是 | 否 |
REPEATABLE READ | 否 | 否 | 是 | 否 |
SERIALIZABLE | 否 | 否 | 否 | 是 |
修改隔離級別的方法
全域性修改
全域性修改需要修改MySql的全域性檔案mysql.ini,修改內容如下
1 #可選引數有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE.
2 [mysqld]
3 transaction-isolation = REPEATABLE-READ
語句修改
在命令列模式下連上MySql後,可以使用下列語句檢視MySql當前隔離級別
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
可以使用下面的命令修改當前會話Session的隔離級
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 事務自動提交
MySql中有AutoCommit引數,預設為on,也就是開啟狀態。它的作用是每一條單獨的查詢都是一個事務,自動開始,自動提交(語句執行完成就提交。如果你要適用select for update,而不手動呼叫 start transaction,這個for update的行鎖機制等於沒用,因為行鎖在自動提交後就釋放了)。所以事務隔離級別和鎖機制即使你不顯式呼叫start transaction
,這種機制在單獨一條語句查詢中也是適用的。
在命令列模式下可以使用下面的命令檢視當前MySql的autocommit是否開啟
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)
如果需要關閉autocommit,我們可以使用下面語句設定
mysql> set autocommit=0;
0就是OFF,1就是ON。設定為OFF之後,則使用者執行語句之後,將一直處於一個事務中,直到執行commit或者rollback,才會結束當前事務,重新開始新的事務。
鎖機制
共享鎖
由讀表操作加上的鎖,加鎖後其他使用者只能獲取該表或行的共享鎖,不能獲取排它鎖,也就是說只能讀不能寫
排它鎖
由寫表操作加上的鎖,加鎖後其他使用者不能獲取該表或行的任何鎖,典型是mysql事務
根據鎖的範圍,可以分為
表鎖
給整張表加鎖
行鎖
給行資料加鎖
因此鎖可以分為表級共享鎖、行級共享鎖、表級排它鎖、行級排它鎖。