成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

詳解 Seata AT 模式事務隔離級別與全局鎖設計

運維 數據庫運維
Seata AT 模式是一種非侵入式的分布式事務解決方案,Seata 在內部做了對數據庫操作的代理層,我們使用 Seata AT 模式時,實際上用的是 Seata 自帶的數據源代理 DataSourceProxy,Seata 在這層代理中加入了很多邏輯,比如插入回滾 undo_log 日志,檢查全局鎖等。

[[442164]]

Seata AT 模式是一種非侵入式的分布式事務解決方案,Seata 在內部做了對數據庫操作的代理層,我們使用 Seata AT 模式時,實際上用的是 Seata 自帶的數據源代理 DataSourceProxy,Seata 在這層代理中加入了很多邏輯,比如插入回滾 undo_log 日志,檢查全局鎖等。

為什么要檢查全局鎖呢,這是由于 Seata AT 模式的事務隔離是建立在支事務的本地隔離級別基礎之上的,在數據庫本地隔離級別讀已提交或以上的前提下,Seata 設計了由事務協調器維護的全局寫排他鎖,來保證事務間的寫隔離,同時,將全局事務默認定義在讀未提交的隔離級別上。

Seata 事務隔離級別解讀

在講 Seata 事務隔離級之前,我們先來回顧一下數據庫事務的隔離級別,目前數據庫事務的隔離級別一共有 4 種,由低到高分別為:

  • Read uncommitted:讀未提交
  • Read committed:讀已提交
  • Repeatable read:可重復讀
  • Serializable:序列化

數據庫一般默認的隔離級別為讀已提交,比如 Oracle,也有一些數據的默認隔離級別為可重復讀,比如 Mysql,一般而言,數據庫的讀已提交能夠滿足業務絕大部分場景了。

我們知道 Seata 的事務是一個全局事務,它包含了若干個分支本地事務,在全局事務執行過程中(全局事務還沒執行完),某個本地事務提交了,如果 Seata 沒有采取任務措施,則會導致已提交的本地事務被讀取,造成臟讀,如果數據在全局事務提交前已提交的本地事務被修改,則會造成臟寫。

由此可以看出,傳統意義的臟讀是讀到了未提交的數據,Seata 臟讀是讀到了全局事務下未提交的數據,全局事務可能包含多個本地事務,某個本地事務提交了不代表全局事務提交了。

在絕大部分應用在讀已提交的隔離級別下工作是沒有問題的,而實際上,這當中又有絕大多數的應用場景,實際上工作在讀未提交的隔離級別下同樣沒有問題。

在極端場景下,應用如果需要達到全局的讀已提交,Seata 也提供了全局鎖機制實現全局事務讀已提交。但是默認情況下,Seata 的全局事務是工作在讀未提交隔離級別的,保證絕大多數場景的高效性。

全局鎖實現

AT 模式下,會使用 Seata 內部數據源代理 DataSourceProxy,全局鎖的實現就是隱藏在這個代理中。我們分別在執行、提交的過程都做了什么。

1、執行過程

執行過程在 StatementProxy 類,在執行過程中,如果執行 SQL 是 select for update,則會使用 SelectForUpdateExecutor 類,如果執行方法中帶有 @GlobalTransactional or @GlobalLock注解,則會檢查是否有全局鎖,如果當前存在全局鎖,則會回滾本地事務,通過 while 循環不斷地重新競爭獲取本地鎖和全局鎖。

  1. public T doExecute(Object... args) throws Throwable { 
  2.     Connection conn = statementProxy.getConnection(); 
  3.     // ... ... 
  4.     try { 
  5.         // ... ... 
  6.         while (true) { 
  7.             try { 
  8.                 // ... ... 
  9.                 if (RootContext.inGlobalTransaction() || RootContext.requireGlobalLock()) { 
  10.                     // Do the same thing under either @GlobalTransactional or @GlobalLock,  
  11.                     // that only check the global lock  here. 
  12.                     statementProxy.getConnectionProxy().checkLock(lockKeys); 
  13.                 } else { 
  14.                     throw new RuntimeException("Unknown situation!"); 
  15.                 } 
  16.                 break; 
  17.             } catch (LockConflictException lce) { 
  18.                 if (sp != null) { 
  19.                     conn.rollback(sp); 
  20.                 } else { 
  21.                     conn.rollback(); 
  22.                 } 
  23.                 // trigger retry 
  24.                 lockRetryController.sleep(lce); 
  25.             } 
  26.         } 
  27.     } finally { 
  28.         // ... 
  29.     } 

2、提交過程

提交過程在 ConnectionProxy#doCommit方法中。

1)如果執行方法中帶有@GlobalTransactional注解,則會在注冊分支時候獲取全局鎖:

  • 請求 TC 注冊分支
  1. private void register() throws TransactionException { 
  2.     if (!context.hasUndoLog() || !context.hasLockKey()) { 
  3.         return
  4.     } 
  5.     Long branchId = DefaultResourceManager.get().branchRegister(BranchType.AT, getDataSourceProxy().getResourceId(), 
  6.                                                                 null, context.getXid(), null, context.buildLockKeys()); 
  7.     context.setBranchId(branchId); 
  • TC 注冊分支的時候,獲取全局鎖
  1. protected void branchSessionLock(GlobalSession globalSession, BranchSession branchSession) throws TransactionException { 
  2.     if (!branchSession.lock()) { 
  3.         throw new BranchTransactionException(LockKeyConflict, String 
  4.                                              .format("Global lock acquire failed xid = %s branchId = %s", globalSession.getXid(), 
  5.                                                      branchSession.getBranchId())); 
  6.     } 

2)如果執行方法中帶有@GlobalLock注解,在提交前會查詢全局鎖是否存在,如果存在則拋異常:

io.seata.rm.datasource.ConnectionProxy#processLocalCommitWithGlobalLocks

  1. private void processLocalCommitWithGlobalLocks() throws SQLException { 
  2.     checkLock(context.buildLockKeys()); 
  3.     try { 
  4.         targetConnection.commit(); 
  5.     } catch (Throwable ex) { 
  6.         throw new SQLException(ex); 
  7.     } 
  8.     context.reset(); 

GlobalLock 注解說明

從執行過程和提交過程可以看出,既然開啟全局事務 @GlobalTransactional注解可以在事務提交前,查詢全局鎖是否存在,那為什么 Seata 還要設計多處一個 @GlobalLock注解呢?

因為并不是所有的數據庫操作都需要開啟全局事務,而開啟全局事務是一個比較重的操作,需要向 TC 發起開啟全局事務等 RPC 過程,而@GlobalLock注解只會在執行過程中查詢全局鎖是否存在,不會去開啟全局事務,因此在不需要全局事務,而又需要檢查全局鎖避免臟讀臟寫時,使用@GlobalLock注解是一個更加輕量的操作。

如何防止臟寫

先來看一下使用 Seata AT 模式是怎么產生臟寫的:

注:分支事務執行過程省略其它過程。

業務一開啟全局事務,其中包含分支事務A(修改 A)和分支事務 B(修改 B),業務二修改 A,其中業務一執行分支事務 A 先獲取本地鎖,業務二則等待業務一執行完分支事務 A 之后,獲得本地鎖修改 A 并入庫,業務一在執行分支事務時發生異常了,由于分支事務 A 的數據被業務二修改,導致業務一的全局事務無法回滾。

如何防止臟寫?

1、業務二執行時加 @GlobalTransactional注解:

注:分支事務執行過程省略其它過程。

業務二在執行全局事務過程中,分支事務 A 提交前注冊分支事務獲取全局鎖時,發現業務業務一全局鎖還沒執行完,因此業務二提交不了,拋異常回滾,所以不會發生臟寫。

2、業務二執行時加 @GlobalLock注解:

注:分支事務執行過程省略其它過程。

與 @GlobalTransactional注解效果類似,只不過不需要開啟全局事務,只在本地事務提交前,檢查全局鎖是否存在。

2、業務二執行時加 @GlobalLock 注解 + select for update語句:

注:分支事務執行過程省略其它過程。

如果加了select for update語句,則會在 update 前檢查全局鎖是否存在,只有當全局鎖釋放之后,業務二才能開始執行 updateA 操作。

如果單單是 transactional,那么就有可能會出現臟寫,根本原因是沒有 Globallock 注解時,不會檢查全局鎖,這可能會導致另外一個全局事務回滾時,發現某個分支事務被臟寫了。所以加 select for update 也有個好處,就是可以重試。

如何防止臟讀

Seata AT 模式的臟讀是指在全局事務未提交前,被其它業務讀到已提交的分支事務的數據,本質上是Seata默認的全局事務是讀未提交。

那么怎么避免臟讀現象呢?

業務二查詢 A 時加 @GlobalLock 注解 + select for update語句:

注:分支事務執行過程省略其它過程。

加select for update語句會在執行 SQL 前檢查全局鎖是否存在,只有當全局鎖完成之后,才能繼續執行 SQL,這樣就防止了臟讀。

本文轉載自微信公眾號「后端進階」,可以通過以下二維碼關注。轉載本文請聯系后端進階公眾號。

 

責任編輯:武曉燕 來源: 后端進階
相關推薦

2010-11-19 16:13:06

oracle事務隔離級

2024-12-02 08:37:04

2009-06-29 17:54:47

Spring事務隔離

2018-12-19 16:46:38

MySQL事務隔離數據庫

2025-01-13 13:12:54

2020-10-13 10:32:24

MySQL事務MVCC

2021-08-04 13:19:42

MySQL 事務隔離

2021-07-26 10:28:13

MySQL事務隔離

2024-04-26 09:17:20

MySQL事務隔離

2022-07-03 14:03:57

分布式Seata

2020-03-05 09:33:15

數據庫事務隔離事務

2020-09-21 18:44:35

MySQL

2022-09-13 13:49:05

數據庫隔離

2021-01-18 11:49:26

面試事務隔離

2025-02-08 10:56:18

2020-02-21 20:10:13

搞懂事務隔離級別

2022-09-19 06:16:23

事務隔離級別Spring

2023-10-11 08:09:53

事務隔離級別

2021-10-19 10:10:51

MySQL事務隔離級別數據庫

2024-02-01 09:18:20

TCC模式Seata
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 夜色www国产精品资源站 | 欧美中文一区 | 亚洲国产成人av好男人在线观看 | 国产三级国产精品 | 九九热精品免费 | 亚洲精品高清视频 | 国产一区二区成人 | 亚洲欧美激情国产综合久久久 | 三级免费毛片 | 亚洲一区二区视频 | 亚洲精品一二区 | 亚洲一区二区三区免费观看 | 国产在线不卡视频 | av入口 | 日韩一级二级片 | 精品国产一区久久 | 日韩综合在线 | 岛国av一区二区三区 | 亚洲精品视频在线看 | 日日干夜夜操天天操 | 国产日韩精品一区二区 | 免费在线观看一级毛片 | 精品亚洲国产成av人片传媒 | 亚洲欧美综合精品久久成人 | 亚洲在线一区二区 | av永久免费 | 欧美a区 | 国产精品1区 | 毛片一区二区 | 福利视频网址 | 亚洲三级av | 亚洲精品电影在线观看 | 天天影视综合 | 一区视频| av大全在线观看 | 午夜性色a√在线视频观看9 | 在线观看av网站永久 | 久久综合久 | av网站免费 | 欧美二区在线 | av一区二区三区在线观看 |