1. 程式人生 > >資料庫四種隔離級別

資料庫四種隔離級別

首先用通俗的語言介紹以下事務的特性(ACID):

  1. 原子性(Atomicity):原子性是指一個事務中的操作,要麼全部成功,要麼全部失敗,如果失敗,就回滾到事務開始前的狀態。

  2. 一致性(Consistency):一致性是指事務必須使資料庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之後都必須處於一致性狀態。那轉賬舉栗子,A賬戶和B賬戶之間相互轉賬,無論如何操作,A、B賬戶的總金額都必須是不變的。

  3. 隔離性(Isolation):隔離性是當多個使用者 併發的 訪問資料庫時,如果操作同一張表,資料庫則為每一個使用者都開啟一個事務,且事務之間互不干擾,也就是說事務之間的併發是隔離的。再舉個栗子,現有兩個併發的事務T1和T2,T1要麼在T2開始前執行,要麼在T2結束後執行,如果T1先執行,那T2就在T1結束後在執行。關於資料的隔離性級別,將在後文講到。

  4. 永續性(Durability):永續性就是指如果事務一旦被提交,資料庫中資料的改變就是永久性的,即使斷電或者宕機的情況下,也不會丟失提交的事務操作。

 

什麼是事務的隔離性(Isolation)呢?

隔離性是指,多個使用者的併發事務訪問同一個資料庫時,一個使用者的事務不應該被其他使用者的事務干擾,多個併發事務之間要相互隔離。

 

如果不考慮隔離性,會發生什麼事呢?

    1.髒讀:

髒讀是指一個事務在處理資料的過程中,讀取到另一個為提交事務的資料。

--原資料
--id    name
--1     lisi

--事務1
START TRANSACTION;
updata t_table set name = 'wangwu' where id = 1;    --此時事務2查詢id = 1
ROLLBACK;

--事務2
select * from t_table where id = 1;        --查詢到 id = 1, name = 'wangwu'

事務1並沒有提交,name 還是 lisi,但是事務2卻讀到了 name = wangwu,這就是髒讀。如果換成A給B轉賬,B查詢到了沒有提交的事務,認為已經收到A轉過來的錢,那豈不是很恐怖。

不過在實際開發中,應該很少有人會犯這樣的低階錯誤。

    2.不可重複讀:

不可重複讀是指對於資料庫中的某個資料,一個事務範圍內的多次查詢卻返回了不同的結果,這是由於在查詢過程中,資料被另外一個事務修改並提交了。

--原資料
--id    name
--1     lisi

--事務1
select * from t_table where id = 1;    -- 查詢到 id = 1, name = list, 事務2在此時提交
select * from t_table where id = 1;    -- 查詢到 id = 1, name = wangwu

--事務2
start transaction;
update t_table set name = 'wangwu' where id = 1;
COMMIT;

不可重複讀和髒讀的區別是,髒讀讀取到的是一個未提交的資料,而不可重複讀讀取到的是前一個事務提交的資料。

而不可重複讀在一些情況也並不影響資料的正確性,比如需要多次查詢的資料也是要以最後一次查詢到的資料為主。

    3.幻讀

幻讀是事務非獨立執行時發生的一種現象。例如事務T1對一個表中所有的行的某個資料項做了從“1”修改為“2”的操作,這時事務T2又對這個表中插入了一行資料項,而這個資料項的數值還是為“1”並且提交給資料庫。而操作事務T1的使用者如果再檢視剛剛修改的資料,會發現還有一行沒有修改,其實這行是從事務T2中新增的,就好像產生幻覺一樣,這就是發生了幻讀。

幻讀和不可重複讀都是讀取了另一條已經提交的事務(這點就髒讀不同),所不同的是不可重複讀查詢的都是同一個資料項,而幻讀針對的是一批資料整體(比如資料的個數)。

--原資料
--id    name
--1     lisi

--事務1
select * from t_table where id = 2;    --返回NULL,此時事務2提交
select * from t_table where id = 2;    --返回id = 2, name = wangwu


--事務2
insert into t_table values(2,'wangwu');
COMMIT;

不可重複讀和幻讀是初學者不易分清的概念,我也是看了詳細的解讀才明白的,總的來說,解決不可重複讀的方法是 鎖行,解決幻讀的方式是 鎖表

 

四種隔離級別解決了上述問題

    1.讀未提交(Read uncommitted):

這種事務隔離級別下,select語句不加鎖。

此時,可能讀取到不一致的資料,即“讀髒 ”。這是併發最高,一致性最差的隔離級別。

    2.讀已提交(Read committed):

可避免 髒讀 的發生。

這是一致性最好的,但併發性最差的隔離級別。

在網際網路大資料量,高併發量的場景下,幾乎 不會使用 上述兩種隔離級別。

    3.可重複讀(Repeatable read):

MySql預設隔離級別。

可避免 髒讀 不可重複讀 的發生。

    4.序列化(Serializable ):

可避免 髒讀、不可重複讀、幻讀 的發生。

 

以上四種隔離級別最高的是 Serializable 級別,最低的是 Read uncommitted 級別,當然級別越高,執行效率就越低。像 Serializable 這樣的級別,就是以 鎖表 的方式(類似於Java多執行緒中的鎖)使得其他的執行緒只能在鎖外等待,所以平時選用何種隔離級別應該根據實際情況。在MySQL資料庫中預設的隔離級別為Repeatable read (可重複讀) 

  在MySQL資料庫中,支援上面四種隔離級別,預設的為Repeatable read (可重複讀) ;而在 Oracle資料庫 中,只支援Serializable (序列化) 級別和 Read committed (讀已提交) 這兩種級別,其中預設的為 Read committed(讀已提交) 級別。

 

查詢和設定資料庫的隔離級別:

select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
set tx_isolation='隔離級別';    --read-uncommitted    read-committed    repeatable-read    serializable
set tx_isolation='read-uncommitted';

select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+