Backend 淺談 database Transaction Isolation Levels

Posted on  Sep 30, 2018  in  資訊技術探索  by  Amo Chen  ‐ 3 min read

在資料庫管理系統中有 4 個很重要的特性被稱為 ACID(Atomicity, Consistency, Isolation, Durability) ,也就是不可分割性、一致性、隔離性、持久性,這 4 個重要特性是為了確保每個 Transaction 操作結果都是正確、可靠的。

其中 Isolation 是為了防止多個 Transactions 同時執行導致資料不一致的情況,而 Isolation 中又有所謂的 Isolation Levels ,根據 SQL-92 的標準分為 4 種級別:

  1. Repeatable Read
  2. Read Committed
  3. Read Uncommitted
  4. Serializable

p.s. MySQL 的 InnoDB 預設的 Isolation Level 是 Repeatable Read

p.s. PostgreSQL 預設的 Isolation Level 是 Read Committed

接著一一介紹這 4 種 Isolation Levels 各自有什麼不同。

Repeatable Read

什麼是 Repeatable Read 呢?

Consistent reads within the same transaction read the snapshot established by the first read. This means that if you issue several plain (nonlocking) SELECT statements within the same transaction, these SELECT statements are consistent also with respect to each other

根據 MySQL 文件說明,如果在同一個 Transaction 內的 nonblocking read 會是第 1 個 read 的 snapshot ,所以相同的 read query 都會取得跟第 1 個 read 一樣的結果。

光看文字其實難以想像,但做個實驗就能知道是怎麼回事。

首先 create table 新增幾筆資料準備做實驗:

CREATE TABLE t (a INT NOT NULL, b INT) ENGINE = InnoDB;
INSERT INTO t VALUES (1,2),(2,3),(3,2),(4,3),(5,2);

接著 SQL 模擬 2 個 Transactions 同時正在執行(p.s. 請開 2 個不同的連線),2 個都 select b = 3 的資料:

-- transaction 1
START TRANSACTION;
SELECT * FROM t WHERE b = 3;

-- transaction 2
START TRANSACTION;
SELECT * FROM t WHERE b = 3;

可以看到 2 個 Transaction 都正常取得 b = 3 的資料:

再來 transaction 1 新增一筆資料 a = 6, b = 3 的資料,然後 commit :

-- transaction 1
INSERT INTO t VALUES (6, 3);
COMMIT;
SELECT * FROM t;

此時資料庫已經有 a = 6, b = 3 的資料了,但在 transaction 2 再 select 一次 b = 3 的情況的話,會發現依然只有 2 筆資料。

-- transaction 2
SELECT * FROM t WHERE b = 3;

這就是先前所提的,在同一個 Transaction 內的 nonblocking read 會是第 1 個 read 的 snapshot ,所以相同的 read query 都會取得跟第 1 個 read 一樣的結果,只要這個 transaction 尚未結束,相同的 query 就會一直重複。

那要怎麼取得最新的資料?很簡單,只要把 transaction 2 commit 之後,再 select 一次 b = 3 的情況就發現結果正確了!因為此時已經不在同一個 transaction 內了!

-- transaction 2
COMMIT;
START TRANSACTION;
SELECT * FROM t WHERE b = 3;

所以未來要是遇到這種 transaction 內的相同 select 總是拿不到最新資料的情況,就要直覺想到是不是 Repeatable Read 的影響。

Read Committed

了解 Repeatable Read 之後,就能很迅速理解 Read Committed 。因為 Read Commited 即使在同一個 Transaction 內使用相同的 select 會拿到最新的資料。

可以利用 Repeatable Read 的實驗再做一次試試,不過 transaction 2 START TRANSACTION; 之前需先執行以下 SQL ,變更 isolation level:

-- transaction 2
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

Read Uncommitted

如其名, Read Uncommitted 可以讀取到其他 transactions 尚未 commit 時的資料,這被稱為 dirty read ,因為讀取尚未 commit 的資料會有些風險,這些資料在 commit 之前仍有可能還會變化,或者最後被 rollback ,導致此時讀取到的資料與資料庫最新狀態不一致。除了 dirty read 這問題之外,其他行為倒跟 Read Committed 一樣。

同樣可以利用 Repeatable Read 的實驗試試,不過 transaction 2 START TRANSACTION; 之前需先執行以下 SQL ,變更 isolation level,然後在 transaction 1 還沒 commit 之前,用 transaction 2 select 看看能不能讀取到 transaction 1 尚未 commit 前的資料。

-- transaction 2
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

Serializable

此 isolation level 在 autocommit 未啟用的情況下,會將所有的 select 都視為 select ... for share 的形式處理。此時,所有的 transactions 都能讀取資料,但是如果有其中 1 個 transaction 欲更新資料,就必須等其他 transactions 都 commit 了才行。另外,如果有其中 1 個 transaction 更新資料但是尚未 commit ,其他的 transactions 如要讀取資料,就得等該 transaction commit 。

Sets a shared mode lock on any rows that are read. Other sessions can read the rows, but cannot modify them until your transaction commits. If any of these rows were changed by another transaction that has not yet committed, your query waits until that transaction ends and then uses the latest values.

可以想見此種 isolation level 是最嚴格的,在效能上也會造成影響,在使用上需多加注意。

總結

本文僅簡單介紹 4 isolation levels ,在各個 DBMS 文件中也提供更深入的介紹,包含在不同的 isolation level 中,會因為不一樣的 locking 有不同的作用,例如 MySQL 的 Transaction Isolation Levels 提到各種 isolation level 中 locking 的差異,建議可以花些時間閱讀。

References

https://en.wikipedia.org/wiki/SQL-92

https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html

https://dev.mysql.com/doc/refman/8.0/en/glossary.html#glos_transaction

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧! PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

追蹤新知

看完這篇文章了嗎?還意猶未盡的話,追蹤粉絲專頁吧!

我們每天至少分享 1 篇文章/新聞或者實用的軟體/工具,讓你輕鬆增廣見聞提升專業能力!如果你喜歡我們的文章,或是想了解更多特定主題的教學,歡迎到我們的粉絲專頁按讚、留言讓我們知道。你的鼓勵,是我們的原力!

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!