微服務系統最大的挑戰:
- 資料的併發訪問、修改;
- 不同請求之間的資料隔離,一個業務請求修改多個資料,保證都完成或失敗;
- 發生異常時的資料回滾。
事務是以一種可靠、一致的方式,訪問和運算元據庫中的資料的程式單元。
以資料庫事務演示為例sql:BEGIN TRANSACTION; --開啟事務
UPDATE t_user SET amount = amount-100 WHERE username='BatMan';
UPDATE t_user SET amount = amount+100 WHERE username='SuperMan';
COMMIT; --提交事務
--ROLLBACK --回滾事務
複製程式碼
其中ResourceManager為事務管理器,JDBC Driver為事務的具體實現,使用不同的驅動則代表不同的事務實現方式。
JDBC事務管理:以java 程式為例子
Connection conn = getConnection(); // 建立資料庫連線
conn.setAutoCommit(false); // 設定不自動提交
Statement stmt1 = conn.prepareStatement(updateUser1Sql);
stmt1.executeUpdate();
Statement stmt2 = conn.prepareStatement(updateUser2Sql);
stmt2.executeUpdate();
conn.commit(); // or conn.rollback(); 提交事務或回滾事務
複製程式碼
這裡演示一個事務的例子: 以轉帳為例子,SuperMan給BatMan轉賬100元,則SuperMan賬戶-100,BatMan賬戶+100。
-- 檢視全域性事務隔離級別,Session事務隔離級別
SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation;
BEGIN TRANSACTION; --開啟事務
UPDATE t_user SET amount = amount-100 WHERE username='BatMan';
UPDATE t_user SET amount = amount+100 WHERE username='SuperMan';
COMMIT; --提交事務
複製程式碼
-- 設定事務隔離級別(不commit也可以讀取最新內容,類似於髒讀)
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRANSACTION; --開啟事務
SELECT * FROM t_user;
SELECT * FROM t_user WHERE username='BatMan';
COMMIT; --提交事務
複製程式碼
以navicat for mysql 工具為例,開啟一個查詢,執行第一段sql ,但最後一行COMMIT還未執行。這時再開一個查詢視窗執行第二段sql,但第2行sql先不執行,從第3行開始執行,此時可以看到結果還是和最初的資料一致,沒有增加也沒有減少,這就是事務的隔離性:一個事務的執行過程中不能影響到其他事務的執行,即一個事務內部的操作及使用的資料對其他事務是隔離的,併發執行各個事務之間無不干擾。 此時全部執行第二段sql,因為設定了事務的隔離級別,所以此時可以看到資料有加有減少,即便第一段sql沒有執行COMMIT操作。
為了解決類似這種資料髒讀問題可以在SELECT 語句後加 FOR UPDATE 是一個排它鎖,也就是說,其他的事務是可以讀取的。但是不能寫入或者更新。效果等同於mysql事務級別中的Serializable。 mysql 預設的事務處理級別是'REPEATABLE-READ',也就是可重複讀。