Apache ShardingSphere 如何實現分散式事務
陸敬尚,Apache ShardingSphere Committer,SphereEx 基礎設施研發工程師,熱愛開源,熱愛資料庫技術,目前專注於 Apache ShardingSphere 事務模組的開發。
背景
隨著業務的快速發展,資料的不斷膨脹,流量負載的增加,業務系統遇到了強烈的挑戰,對資料庫系統可擴充套件性提出了強烈的訴求。Oracle、MySQL、SQL Server、PostgreSQL 傳統單機資料庫線上擴充套件上的問題日益凸顯。為了解決擴充套件問題,出現了可水平擴充套件的分散式資料庫,於是分散式事務問題成為了必須面對的問題。
在這種背景下,ShardingSphere 提供了一套分散式資料庫增強計算引擎,通過可插拔架構構建基於資料庫之上的生態系統,提供了分散式事務的能力。
事務介紹
事務語義
事務語義定義了四個特性:原子性(Atomicity)、永續性(Durability)、一致性(Consistency)、隔離性(Isolatation)。
原子性(Atomicity)
在分散式場景下,一個事務的操作可能分佈在多個物理節點上,保證在多個節點上的操作都成功,或都不成功。
永續性(Durability)
事務提交後,即使斷電,事務的操作也是有效的。
一致性(Consistency)
注意:不是 CAP 理論中 C,CAP 中的 C 指的是多副本之間的資料一致問題,這裡是不同層次的抽象。
站在使用者的角度,資料從一個狀態,轉移到另外一個狀態,兩個狀態都滿足一定的約束。比如:
銀行賬戶資料,賬戶 A 有 500 元,賬戶 B 有 500 元,總額 1000,在一個事務中,執行完 A 和 B 的轉賬操作後,A 和 B 的賬戶總額還是 1000。
隔離性(Isolatation)
事務併發執行時,保證併發時資料的正確性。比如:兩個事務同時修改一條資料,保證兩個事務按一定順序執行,使資料保持在一個正確的狀態。
面臨的挑戰
分散式事務相對單機事務來說面臨下面的挑戰:
-
原子性,對於單機事務來說,使用 undo log 和 redo log 就可以保證全部提交或者全部回滾。而分散式事務涉及多個物理節點,每個節點情況是不同的,有的節點日誌寫成功,有的節點日誌寫不成功。
-
網路的不穩定,對於單機來說,通訊是穩定的,任何操作都可以得到回覆,不論成功與失敗。而分散式場景下,網路是不穩定的,有可能一個操作是得不到回覆的,怎樣保證分散式事務的可用性(異常事務的清理、恢復等)是一個問題。
-
併發控制,隨著 MVCC 的出現,操作的可線性化(linearizable)成為了剛需。在單機資料庫中,可以很容易地產生全域性單調遞增的事務號,在分散式場景中則不然。
解決方案
原子提交
針對原子性和網路不穩定問題,目前主流的解決方案是 2PC,2PC 定義了兩個角色 TM(Transaction Manager)、RM(Resource Manager)。
在分散式場景下,一個事務的操作可能分佈在多個節點上,整個事務分兩個階段。
-
第一階段,RM 鎖定相關資源並執行具體操作,返回成功與否給 TM。
-
第二階段,TM 更具第一階段 RM 返回的結果,如果全部成功,執行最後的提交操作(事務狀態的更改,鎖狀態刪除等),如果有失敗的,則回滾。
說明:當然會有一些優化點,比如不涉及多節點的事務轉化為一階段提交等。
注意:兩階段提交協議只解決了提交的問題,要麼提交成功,要麼不成功,不存在部分成功的中間狀態。和事務隔離級別沒有必然關係。
併發控制
併發控制,就是保證併發執行的事務在某一種隔離級別上的執行策略。自從多版本控制(MVCC)出現,主流資料庫基本拋棄了以前的兩階段鎖模型。
併發控制本質是對資料讀和寫的併發的控制。併發控制的策略決定了隔離級別,併發控制要解決兩個問題。
-
決定併發的粒度,比如 MySQL 有行鎖(粒度為一行),表鎖(粒度為一個表)等
-
三種併發場景的行為:
a. 讀讀併發,不需要特殊處理,因為不涉及資料的變更。
b. 寫寫併發,不能同時併發,否則會產生資料混亂。
c. 讀寫併發,效能優化主要在這裡做,有多種併發控制機制,基本都選擇了多版本併發控制(MVCC)。
MVCC 併發控制模型
現有主流實現方式有兩種:
- 基於事務 ID 和 ReadView
每次事務獲取事務 ID,標識事務的開啟順序,通過活躍事務列表來獲取快照,儲存多個以事務 ID 為版本的資料,從而達到併發控制的效果。MySQL、Postgres-XL 都是採取的這種方案。
- 基於 timestamp
引入 timestamp,通過在資料中新增 timestamp 相關屬性,通過對比資料的 commitTs(commit timestamp) 和 Snapshot timestamp 來判斷可見性,從而達到可線性化的併發控制效果。Spanner 採用的這種方案。
上面兩種方案都離不開全域性事務號的生成,常見的全域性事務號生成機制有 TrueTime(Spanner 採用),HLC(CockroachDB 採用有誤差的 HLC),TSO(Timestamp Oracle),詳細原理見參考文獻。
ShardingSphere 事務設計
ShardingSphere 事務功能建立在儲存 DB 的本地事務之上,提供 LOCAL、XA、BASE 三種模式事務,使用者只需使用原生事務方式(begin/commit/rollback),就可以使用三種模式,在一致性和效能做合適的權衡。
LOCAL
LOCAL 模式直接建立在儲存 DB 的本地事務上的,會存在一定的原子性問題,當然效能是最高的,如果可以容忍這個問題,這將是一個不錯的選擇。
XA
XA 模式,XA 協議是基於 2PC 定義的一套互動協議,定義了 xa start/prepare/end/commit/rollback 等介面,常用的實現有 Narayana、Atomics, ShardingSphere 整合了 Narayana、Atomics 的 XA 實現。
-
app 連線到 Proxy 上,Proxy 建立一個 session 的物件和這個 connection 繫結。
-
app 執行 begin,Proxy 通過 Narayana TM 新建一個邏輯 transaction,和當前 session 繫結。
-
app 執行具體 SQL,session 負責建立到儲存 DB 的 connection,並把 connection 通過 Transaction.enlistResource() 介面把 connection 註冊到 transaction,執行 XA START {XID} 開啟事務,並執行路由改寫後的 SQL。
-
app 執行 commit 命令,transaction 中註冊的連線儲存 DB 的 connection,分別執行 xa prepare,當所有 connection 返回 ok,更新 transaction 狀態為 prepared,每個 connection 執行 xa commit,都返回 ok 更新 transaction 狀態為 commited,提交成功。如果 prepare 過程部分失敗,使用者可以通過 rollback 命令出發回滾,不處理則有後臺程式進行清理。
-
app 執行 rollback 命令,transaction 中註冊的連線儲存 DB 的 connection,分別執行 xa rollback,進行回滾。
BASE
Base(Basically Available, Soft State, Eventually Consistent)模式,BASE 事務是 CAP 理論中 C 和 A 權衡的結果,Seata 的 AT 模式是 Base 事務的一種實現,ShardingSphere 整合了 Seata 的 AT 實現。
-
app 連線到 Proxy 上,Proxy 建立一個 session 的物件和這個 connection 繫結。
-
app 執行 begin,Proxy 通過 Seata TM 新建一個邏輯 transaction,和當前 session 繫結,並註冊到 Seata Server。
-
app 執行一條邏輯 SQL,session 負責建立到儲存 DB 的 connection,每個 connection 是 Seata 的 ConnectionProxy 例項,對路由改寫後的 actual sql 進行解析, 做一些攔截,比如:如果是修改操作,執行 begin 獲取本地鎖,執行一條 SQL, 執行 commit 釋放本地鎖,上報分支事務結果到 Seata Server。
-
app 執行 commit 命令,Proxy 中的 Seata TM 通知 Seata Server 後,直接返回 app,Seata Server 非同步和 Proxy 互動,進行刪除事務日誌。
-
app 執行 rollback 命令,Proxy 中的 Seata TM 通知 Seata Server 後,直接返回 app,Seata Server 非同步和 Proxy 互動,執行補償操作,刪除事務日誌。
詳細流程參考 Seata 官網。
使用示例
安裝包準備
以支援較好的 XA,整合 Narayana 的實現為例,進行介紹。由於 Narayana 的 License 問題,不能直接打包到安裝包內,需要新增額外的依賴。
根據官網下載好安裝包,解壓到 ${ShardingSphere} 目錄,往 ${ShardingSphere}/lib 目錄下新增以下 jar 包。(下載地址: )
jta-5.12.4.Final.jar arjuna-5.12.4.Final.jar common-5.12.4.Final.jar jboss-connector-api_1.7_spec-1.0.0.Final.jar | ------------------------------------------------------------------------------------------------------------------------------------ jboss-logging-3.2.1.Final.jar | ------------------------------------------------------------------------------------------------------------------------------------ jboss-transaction-api_1.2_spec-1.0.0.Alpha3.jar | ------------------------------------------------------------------------------------------------------------------------------------ jboss-transaction-spi-7.6.0.Final.jar mysql-connector-java-5.1.47.jar | ------------------------------------------------------------------------------------------------------------------------------------ narayana-jts-integration-5.12.4.Final.jar shardingsphere-transaction-xa-narayana-5.1.1-SNAPSHOT.jar
MySQL 例項準備
-
準備兩個 MySQL 例項,127.0.0.1:3306,127.0.0.1:3307。
-
兩個 MySQL 例項分別建立使用者 root, 密碼為 12345678。
-
兩個 MySQL 例項分別建立 test 庫。
ShardingSphere-Proxy 配置
修改 server.yaml 事務配置
rules: - !AUTHORITY users: - root@%:root - sharding@:sharding provider: type: ALL_PRIVILEGES_PERMITTED - !TRANSACTION defaultType: XA providerType: Narayana
修改 conf/conf-sharding.yaml
dataSources: ds_0: url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&allowPublicKeyRetrieval=true&useSSL=false username: root password: 12345678 connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 minPoolSize: 1 ds_1: url: jdbc:mysql://127.0.0.1:3307/test?serverTimezone=UTC&allowPublicKeyRetrieval=true&useSSL=false username: root password: 12345678 connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 minPoolSize: 1 rules: - !SHARDING tables: account: actualDataNodes: ds_${0..1}.account${0..1} tableStrategy: standard: shardingColumn: id shardingAlgorithmName: account_inline keyGenerateStrategy: column: id keyGeneratorName: snowflake defaultDatabaseStrategy: standard: shardingColumn: id shardingAlgorithmName: database_inline defaultTableStrategy: none: shardingAlgorithms: database_inline: type: INLINE props: algorithm-expression: ds_${id % 2} account_inline: type: INLINE props: algorithm-expression: account${id % 2} keyGenerators: snowflake: type: SNOWFLAKE props: worker-id: 123
啟動ShardingSphere-Proxy
參考如下命令啟動 Proxy:
cd ${ShardingSphere}./bin/start.sh
使用 ShardingSphere-Proxy
使用 MySQL Client 連線 ShardingSphere-Proxy 進行測試,參考如下命令。
mysql -h127.0.0.1 -P3307 -uroot -proot
mysql> use sharding_db; Database changed mysql> create table account(id int, balance float ,transaction_id int); Query OK, 0 rows affected (0.12 sec) mysql> select * from account;Empty set (0.02 sec) mysql> begin; Query OK, 0 rows affected (0.09 sec) mysql> insert into account(id, balance, transaction_id) values(1,1,1),(2,2,2); Query OK, 2 rows affected (0.53 sec) mysql> select * from account; +------+---------+----------------+ | id | balance | transaction_id | +------+---------+----------------+ | 2 | 2.0 | 2 | | 1 | 1.0 | 1 | +------+---------+----------------+2 rows in set (0.03 sec) mysql> commit; Query OK, 0 rows affected (0.05 sec) mysql> select * from account; +------+---------+----------------+ | id | balance | transaction_id | +------+---------+----------------+ | 2 | 2.0 | 2 | | 1 | 1.0 | 1 | +------+---------+----------------+2 rows in set (0.02 sec)
未來規劃
現在 ShardingSphere 的分散式事務整合了第三方的 2PC 實現方案,提供了原子性的保證,隔離性依賴於儲存 DB 的隔離保證,提供了可用的事務功能。未來基於全域性 Timestamp 實現 MVCC,結合 2PC,對事務隔離語義提供更好的支援。歡迎大家關注 ShardingSphere 的成長。
如果大家對 Apache ShardingSphere 有任何疑問或建議,歡迎在 GitHub issue 列表提出,或可前往中文社群交流討論。
GitHub issue:
貢獻指南:
中文社群:
參考文獻
1.ACID wiki:
2.ANSI isolation levels: https://renenyffenegger.ch/notes/development/databases/SQL/transaction/isolation-level
3.《Distributed Transactions Are Evil》:
4.《Distributed transactions and why you should care》:
5.《Fault-Tolerant Stream Processing at Internet Scale》:
6.《Oracle Two Phase Commit 2PC Tips》:
7.《2PC: Concurrency Control and Recovery in Database Systems》:
8.《An Empirical Evaluation of In-Memory Multi-Version Concurrency Control》:
9.《Concurrency Control And Recovery In Database Systems》:
10.《Optimistic Concurrency Control》:
11.《Base: An Acid Alternative》:
12.《Jepsen: CockroachDB beta-20160829》:
13.《Spanner: Google’s Globally-Distributed Database》:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70001955/viewspace-2887871/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- ShardingSphere如何輕鬆駕馭Seata柔性分散式事務?分散式
- 分散式事務(3)---RocketMQ實現分散式事務原理分散式MQ
- 分散式事務(4)---RocketMQ實現分散式事務專案分散式MQ
- Apache Pulsar分散式事務機制Apache分散式
- 基於RocketMQ實現分散式事務MQ分散式
- 使用Spring Boot實現分散式事務Spring Boot分散式
- SpringCloud+RocketMQ實現分散式事務SpringGCCloudMQ分散式
- 實戰與原理:如何基於RocketMQ實現分散式事務?MQ分散式
- go-zero微服務實戰系列(十、分散式事務如何實現)Go微服務分散式
- 如何實現跨Mysql、Redis和Mongo分散式事務? - dongfuMySqlRedisGo分散式
- 14-ShardingSphere的分散式主鍵實現分散式
- 分散式事務實戰分散式
- 分散式事務之資料庫事務與JDBC事務實現(一)分散式資料庫JDBC
- 分散式事務的幾種實現方式分散式
- AspNetCore&MassTransit Courier實現分散式事務NetCore分散式
- Golang 實現 Redis(8): TCC分散式事務GolangRedis分散式
- php基於dtm分散式事務管理器實現tcc模式分散式事務demoPHP分散式模式
- 分散式事務(一)—分散式事務的概念分散式
- 資料庫分散式事務的實現原理!資料庫分散式
- 分散式事務之JTA原理與實現(三)分散式
- kratos分散式事務實踐分散式
- 如何實現一個TCC分散式事務框架的一點思考分散式框架
- 分散式事務之事務實現模式與技術(四)分散式模式
- 事務使用中如何避免誤用分散式事務分散式
- Python如何快速實現分散式任務?Python分散式
- Seata 無侵入式分散式事務服務的實現基石-JDBC篇分散式JDBC
- MySQL 中基於 XA 實現的分散式事務MySql分散式
- Dubbo 分散式事務一致性實現分散式
- Laravel基於reset機制實現分散式事務Laravel分散式
- Asp.Net Core&CAP實現分散式事務ASP.NET分散式
- 基於 Apache ShardingSphere 構建高可用分散式資料庫Apache分散式資料庫
- MassTransit | 基於StateMachine實現Saga編排式分散式事務Mac分散式
- 微服務架構 | 11.1 整合 Seata AT 模式實現分散式事務微服務架構模式分散式
- 分散式事務和分散式hash分散式
- 使用CRDT實現分散式事務的資料推薦分散式
- node.js 中使用redis實現分散式事務鎖Node.jsRedis分散式
- Spring Cloud Seata系列:基於AT模式實現分散式事務SpringCloud模式分散式
- 基於Seata探尋分散式事務的實現方案分散式