分布式事务执行的三种方式

举报
码乐 发表于 2025/02/21 09:45:51 2025/02/21
【摘要】 1 简介本文结束分布式事物中的交易处理与并发控制。如2PC两阶段事务提交算法,以及是关于如何在web服务中实现分布式事务的指导,以及主要分布式事务控制方法的简介。 分布式事务控制:如两阶段提交协议。在web服务中实现分布式事务是一项复杂的任务,涉及多个服务或数据库间的一致性保证。实现分布式事务时,通常需要一个事务控制机制,例如 两阶段提交协议 (2PC)、三阶段提交协议 (3PC)、或现...

1 简介

本文结束分布式事物中的交易处理与并发控制。如2PC两阶段事务提交算法,以及是关于如何在web服务中实现分布式事务的指导,以及主要分布式事务控制方法的简介。

		分布式事务控制:如两阶段提交协议。

在web服务中实现分布式事务是一项复杂的任务,涉及多个服务或数据库间的一致性保证。

实现分布式事务时,通常需要一个事务控制机制,例如 两阶段提交协议 (2PC)、三阶段提交协议 (3PC)、或现代的 Saga 模式。

2 分布式事务控制方法

  1. 两阶段提交协议 (2PC)

特点:

经典的分布式事务方法。

	分两个阶段:准备阶段 和 提交阶段。

中心化的事务协调器控制所有事务分支。

优点:

保证一致性。

缺点:

阻塞性:资源可能长时间被锁住。
单点故障风险:事务协调器的故障可能导致事务卡住。

2. 三阶段提交协议 (3PC)

特点:

解决 2PC 阻塞问题。
增加了一个 "预提交阶段",在正式提交前进行一致性检查。

优点:

减少事务阻塞时间。

缺点:

增加了复杂性和延迟。
网络分区时可能仍会出现一致性问题。

3. Saga 模式

特点:

将事务拆分成一系列可补偿的小事务。
每个操作都有一个对应的补偿操作。
使用协调器或消息队列管理事务链。

优点:

非阻塞,适合长事务。
易于扩展,解耦。

缺点:

编程复杂度增加。
数据最终一致性而非强一致性。

在web服务中实现两阶段提交 (2PC)
以下是一个基于 MySQL 的分布式事务实现两阶段提交的示例。

依赖安装

    go get -u github.com/gin-gonic/gin
    go get -u github.com/go-sql-driver/mysql
    go get -u github.com/sirupsen/logrus

4 实现示例

    1. 数据库表设计

在每个数据库中创建一个记录事务状态的表:

      CREATE TABLE transaction_logs (
          id VARCHAR(64) PRIMARY KEY,    -- 事务ID
          state ENUM('PREPARED', 'COMMITTED', 'ABORTED') NOT NULL, -- 事务状态
          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
      );
    1. 服务代码实现

         type DB struct {
             conn *sql.DB
         }
      
         var db1, db2 *DB
      
         func initDB(dsn string) *DB {
             conn, err := sql.Open("mysql", dsn)
             if err != nil {
                 log.Fatalf("Failed to connect to database: %v", err)
             }
             return &DB{conn: conn}
         }
      
         func (db *DB) prepareTransaction(txID string) error {
             _, err := db.conn.Exec("INSERT INTO transaction_logs (id, state) VALUES (?, 'PREPARED')", txID)
             return err
         }
      
         func (db *DB) commitTransaction(txID string) error {
             _, err := db.conn.Exec("UPDATE transaction_logs SET state = 'COMMITTED' WHERE id = ?", txID)
             return err
         }
      
         func (db *DB) abortTransaction(txID string) error {
             _, err := db.conn.Exec("UPDATE transaction_logs SET state = 'ABORTED' WHERE id = ?", txID)
             return err
         }
      
         func main() {
             db1 = initDB("user:password@tcp(127.0.0.1:3306)/db1")
             db2 = initDB("user:password@tcp(127.0.0.1:3306)/db2")
             defer db1.conn.Close()
             defer db2.conn.Close()
      
             r := gin.Default()
      
             r.POST("/transaction", func(c *gin.Context) {
                 txID := fmt.Sprintf("%d", time.Now().UnixNano())
      
                 // 第一阶段:准备
                 if err := db1.prepareTransaction(txID); err != nil {
                     c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to prepare db1"})
                     return
                 }
                 if err := db2.prepareTransaction(txID); err != nil {
                     db1.abortTransaction(txID) // 回滚 db1
                     c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to prepare db2"})
                     return
                 }
      
                 // 第二阶段:提交
                 if err := db1.commitTransaction(txID); err != nil {
                     db1.abortTransaction(txID) // 回滚 db1
                     db2.abortTransaction(txID) // 回滚 db2
                     c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to commit db1"})
                     return
                 }
                 if err := db2.commitTransaction(txID); err != nil {
                     db1.abortTransaction(txID) // 回滚 db1
                     db2.abortTransaction(txID) // 回滚 db2
                     c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to commit db2"})
                     return
                 }
      
                 c.JSON(http.StatusOK, gin.H{"message": "Transaction committed successfully"})
             })
      
             r.Run(":8080")
         }
      
  • 在web服务中实现 Saga 模式

Saga 模式适用于非强一致性的场景,例如跨服务的支付与库存系统。

核心步骤

将事务拆分成多个小事务(每个服务单独完成一个事务)。
为每个事务设计补偿操作。
使用消息队列协调事务流程。

5 小结

分布式事务对比

    方法	一致性	性能	适用场景	复杂性
    2PC	强一致性	低	银行转账、核心交易	中等
    3PC	强一致性	中	高可靠性场景	较高
    Saga	最终一致性	高	电商订单、微服务系统	较高

通过选择适合的事务控制方法,并在web服务中结合合适的数据库和协调器,可以有效地实现分布式事务控制。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。