天天看點

分布式事務開局第一篇,從資料庫事務隔離級别說起

春節臨近,松哥也有點無心撸碼。不過很多時候,很多事情,我們不能由着自己的性子,還是要控制一下自己,這不,松哥最近又打算開一個坑,和大家聊一聊分布式事務,因為我們做微服務,分布式事務肯定是跳不過去的坎。這個坑有點大,年前先更幾篇。

說到分布式事務,很多小夥伴可能會想到 TCC ,但是實際上,分布式事務本身是一個比較大的話題,一步一步從頭開始講,會涉及到資料庫事務、Jdbc 事務、Spring 事務、消息驅動模式處理事務、事件溯源模式等等很多種。

松哥想寫一個類似于 SpringBoot 那樣成體系的教程,來和大家仔細的捋一捋分布式事務,今天我們就先從資料庫事務開始。(另外,大家在公衆号背景分别回複

spring

springmvc

mybatis

springboot

maven

可以下載下傳松哥手撸的幹貨教程。)

1. 理論

MySQL 中事務的隔離級别一共分為四種,分别如下:

  • 序列化(SERIALIZABLE)
  • 可重複讀(REPEATABLE READ)
  • 送出讀(READ COMMITTED)
  • 未送出讀(READ UNCOMMITTED)

四種不同的隔離級别含義分别如下:

  1. SERIALIZABLE
如果隔離級别為序列化,則使用者之間通過一個接一個順序地執行目前的事務,這種隔離級别提供了事務之間最大限度的隔離。
  1. REPEATABLE READ
在可重複讀在這一隔離級别上,事務不會被看成是一個序列。不過,目前正在執行事務的變化仍然不能被外部看到,也就是說,如果使用者在另外一個事務中執行同條 SELECT 語句數次,結果總是相同的。(因為正在執行的事務所産生的資料變化不能被外部看到)。
  1. READ COMMITTED
READ COMMITTED 隔離級别的安全性比 REPEATABLE READ 隔離級别的安全性要差。處于 READ COMMITTED 級别的事務可以看到其他事務對資料的修改。也就是說,在事務處理期間,如果其他事務修改了相應的表,那麼同一個事務的多個 SELECT 語句可能傳回不同的結果。
  1. READ UNCOMMITTED
READ UNCOMMITTED 提供了事務之間最小限度的隔離。除了容易産生虛幻的讀操作和不能重複的讀操作外,處于這個隔離級的事務可以讀到其他事務還沒有送出的資料,如果這個事務使用其他事務不送出的變化作為計算的基礎,然後那些未送出的變化被它們的父事務撤銷,這就導緻了大量的資料變化。

在 MySQL 資料庫種,預設的事務隔離級别是 REPEATABLE READ

2. 實戰

接下來通過幾條簡單的 SQL 向讀者驗證上面的理論。

2.1 檢視隔離級别

通過如下 SQL 可以檢視資料庫執行個體預設的全局隔離級别和目前 session 的隔離級别:

SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
           

查詢結果如圖:

分布式事務開局第一篇,從資料庫事務隔離級别說起

1-1

可以看到,預設的隔離級别為 REPEATABLE-READ,通過如下指令可以修改隔離級别(建議開發者在修改時修改目前 session 隔離級别即可):

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
           

上面這條 SQL 表示将目前 session 的資料庫隔離級别設定為 READ UNCOMMITTED,設定成功後,再次查詢隔離級别,發現目前 session 的隔離級别已經變了,如圖1-2:

分布式事務開局第一篇,從資料庫事務隔離級别說起

1-2

注意,如果隻是修改了目前 session 的隔離級别,則換一個 session 之後,隔離級别又會恢複到預設的隔離級别

2.2 READ UNCOMMITTED

READ UNCOMMITTED 是最低隔離級别,這種隔離級别中存在髒讀、不可重複讀以及幻象讀問題,下面分别予以介紹。

首先建立一個簡單的表,預設兩條資料,如下:

分布式事務開局第一篇,從資料庫事務隔離級别說起

1-3

表的資料很簡單,有 zhangsan 和 lisi 兩個使用者,兩個人的賬戶各有 1000 人民币。現在模拟這兩個使用者之間的一個轉賬操作。

注意,如果讀者使用的是 Navicat 的話,不同的查詢視窗就對應了不同的 session,如果讀者使用了 SQLyog 的話,不同查詢視窗對應同一個 session,是以如果使用 SQLyog,需要讀者再開啟一個新的連接配接,在新的連接配接種進行查詢操作。

2.2.1 髒讀

一個事務讀到另外一個事務還沒有送出的資料,稱之為髒讀。具體操作如下:

1.首先打開兩個SQL操作視窗,假設分别為 A 和 B,在 A 視窗中輸入如下幾條 SQL (輸入完成後不用執行):

START TRANSACTION;
UPDATE user set account=account+100 where username='zhangsan';
UPDATE user set account=account-100 where username='lisi';
COMMIT;
           

2.在 B 視窗執行如下 SQL,修改預設的事務隔離級别為 READ UNCOMMITTED,如下:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
           

3.接下來在 B 視窗中輸入如下 SQL,輸入完成後,首先執行第一行開啟事務(注意隻需要執行一行即可):

START TRANSACTION;
SELECT * from user;
COMMIT;
           

4.接下來執行 A 視窗中的前兩條 SQL,即開啟事務,給 zhangsan 這個賬戶添加 100 元。

5.進入到 B 視窗,執行 B 視窗的第二條查詢 SQL(SELECT * from user;),結果如下:

分布式事務開局第一篇,從資料庫事務隔離級别說起

1-4

可以看到,A 視窗中的事務,雖然還未送出,但是 B 視窗中已經可以查詢到資料的相關變化了。

這就是髒讀問題。

2.2.2 不可重複讀

不可重複讀是指一個事務先後讀取同一條記錄,但兩次讀取的資料不同,稱之為不可重複讀。具體操作步驟如下(操作之前先将兩個賬戶的錢都恢複為1000):

1.首先打開兩個查詢視窗 A 和 B ,并且将 B 的資料庫事務隔離級别設定為 READ UNCOMMITTED。具體 SQL 參考上文,這裡不贅述。

2.在 B 視窗中輸入如下 SQL,然後隻執行前兩條 SQL 開啟事務并查詢 zhangsan 的賬戶:

START TRANSACTION;
SELECT * from user where username='zhangsan';
COMMIT;
           

前兩條 SQL 執行結果如下:

分布式事務開局第一篇,從資料庫事務隔離級别說起

1-5

3.在 A 視窗中執行如下 SQL,給 zhangsan 這個賬戶添加 100 塊錢,如下:

START TRANSACTION;
UPDATE user set account=account+100 where username='zhangsan';
COMMIT;
           

4.再次回到 B 視窗,執行 B 視窗的第二條 SQL 檢視 zhangsan 的賬戶,結果如下:

分布式事務開局第一篇,從資料庫事務隔離級别說起

1-6

zhangsan 的賬戶已經發生了變化,即前後兩次檢視 zhangsan 賬戶,結果不一緻。不可重複讀強調的是其他事務對資料進行了修改或者删除,這一點注意和幻象讀進行區分。

2.2.3 幻象讀

幻象讀和不可重複讀比較像,強調了不同方面。幻象讀是指當一個事務根據條件查詢資料時,另外一個事務插入一條新記錄,這條新資料恰好可以滿足第一個事務的查詢條件,然後此時再次執行第一個事務,就會看到第二個事務插入的新記錄,這個新記錄就稱為“幻象”。

例如,執行如下SQL查詢目前表的使用者數量:

START TRANSACTION;
SELECT COUNT(*) FROM user;
COMMIT;
           

擷取到的結果為 2,表示有兩個使用者。

此時在一個新的事務中,向表中添加新記錄,然後再次執行這裡第二行統計 SQL,就會查到三個使用者,這個比較簡單,本文就不示範了。

2.3 READ COMMITTED

和 READ UNCOMMITTED 相比,READ COMMITTED 主要解決了髒讀的問題,對于不可重複讀和幻象讀則未解決。操作案例與上文一緻,這裡不再贅述。

2.4 REPEATABLE READ

和 READ COMMITTED 相比,REPEATABLE READ 進一步解決了不可重複讀的問題,但是幻象讀則未解決。操作案例與上文一緻,這裡不再贅述。

注意,REPEATABLE READ 也是 InnoDB 引擎的預設資料庫事務隔離級别

2.5 SERIALIZABLE

SERIALIZABLE 提供了事務之間最大限度的隔離,在這種隔離級别中,事務一個接一個順序的執行,不會發生髒讀、可不重複讀以及幻象讀問題。最安全。相關操作案例與上文一緻,這裡不再贅述。

3. 總結

總的來說,隔離級别和髒讀、不可重複讀以及幻象讀的對應關系如下:

隔離級别 髒讀 不可重複讀 幻象讀
READ UNCOMMITTED 允許 允許 允許
READ COMMITED 不允許 允許 允許
REPEATABLE READ 不允許 不允許 允許
SERIALIZABLE 不允許 不允許 不允許

性能關系如圖:

分布式事務開局第一篇,從資料庫事務隔離級别說起

1-7

好了,這篇文章就和小夥伴們先說這麼多,大家不妨寫幾行 SQL 試一試。

分布式事務開局第一篇,從資料庫事務隔離級别說起

1. 全棧架構之打包推薦【建議收藏,常讀】

2. 探讨確定消息消費幂等性的幾種方式

3. 分布式系統中Session共享的常用方案

4. Java語言“坑爹”排行榜TOP 10

5. 我是一個Java類(附帶精彩吐槽)

6. mysql索引失效,差點我的工作涼了

7. 既生synchronized,何生volatile?

8. 微服務一直火,為什麼服務化要搞懂?

9. MySQL的COUNT語句,不簡單!

10. 漫畫:HashSet和TreeSet實作與原理

分布式事務開局第一篇,從資料庫事務隔離級别說起
分布式事務開局第一篇,從資料庫事務隔離級别說起

掃碼二維碼關注我

·end·

—如果本文有幫助,請分享到朋友圈吧—

我們一起愉快的玩耍!

分布式事務開局第一篇,從資料庫事務隔離級别說起

你點的每個贊,我都認真當成了喜歡