分布式事务的解决方案

数据库事务是多个SQL构成一个业务整体,必须同时提交或同时回滚。而分布式事务中,一个大操作由多个小操作组成,各个小操作处于不同的物理节点(或进程),这些小操作必须作为一个整体,同时提交或同时回滚。本文总结了事务的特性,以及 MySQL/Redis/MQ 的分布式事务的具体解决方案。

作者:王克锋
出处:https://kefeng.wang/2018/03/01/distributed-transaction/
版权:自由转载-非商用-非衍生-保持署名,转载请标明作者和出处。

1 存在场景

根本上说,最终归结于数据存储被拆分,需要保证数据存储一致性。

  • 数据存储被拆分:数据库(MySQL)分库分表、缓存(Redis)分节点分库、消息(RocketMQ)多节点
  • 业务功能被拆分:多子系统、SOA 服务化应用

2 事务的 ACID 特性

2.1 事务的 ACID

参考资料:ACID - 维基百科
ACID 的概念在 ISO/IEC 10026-1:1992 文件的第四段内有所说明。
ACID 是指数据库管理系统(DBMS)在进行写操作时,为保证事务正确可靠,必须具备的四个特性:

  • 原子性(Atomicity): 事务是个不可拆分的整体,其中的各操作要么全部执行,要么全部回滚至执行前的状态,不会处于中间状态;
  • 一致性(Consistency): 无论事务开始之前,还是事务结束之后(无论提交还是回滚),数据库都能保证各数据一致性;
  • 隔离性(Isolation): 允许多个事务并发对数据进行读写操作,各事务相互隔离互不干扰,一个事务看不到另一个事务尚未提交的数据变化;
  • 持久性(Durability): 事务结束后(无论提交还是回滚),结果数据都会持久化,即使系统故障,数据也不会丢失。

2.2 事务隔离级别

参考资料:事务隔离 - 维基百科
对于隔离性,事务隔离(Transaction Isolation)定义了一个事务的操作结果,在何时以何种方式,可以被其他事务看到。
不同 DBMS 默认的隔离级别是不一样的(MySQL 默认为 Repeatable Read),大多数据库允许用户指定。
根据 ANSI/ISO SQL 规范,事务的隔离级别分为:

  • 串行化(Serializable): 最高的隔离级别(性能最差),在选定对象上的读锁和写锁持有直到事务结束后才释放,要求“范围锁”(Range-Locks);
  • 可重复的读(Repeatable Read): 含义是同一行数据读取多次的结果相同。对选定对象的读锁和写锁一直保持到事务结束,不要求“范围锁”;
  • 提交的读(Read Committed): 对选定对象的写锁一直保持到事务结束,但是读锁在读操作完成后马上释放,不要求“范围锁”;
  • 未提交的读(Read Uncommitted): 最低的隔离级别,允许“脏读”(Dirty Reads),一个事务可以看到其他事务“尚未提交”的修改。

3 MySQL 分布式事务(XA)

3.1 MySQL 分布式构架方案

MySQL 的分布式可以采用两种架构方案:

  • 中间代理: 由第三方软件实现 MySQL 节点的汇合,比如 MyCAT;
  • 客户端: 客户端实现 MySQL 节点的汇合,该方案对业务有侵入,下面指的是本情景;

3.2 分布式事务协议 XA

XA 是 X/Open DTP 组织 1994 年定义的分布式事务协议,协议规范如下:
THE XA SPECIFICATIONTHE XA+ SPECIFICATION, VERSION 2

采用两阶段提交(2PC, Two Phase Commitment)协议:

  • Prepare: 征询每个节点,其事务是否可提交,若可提交则资源本地记录操作并响应为同意,若不可提交则回滚并响应为拒绝;
  • Commit/Rollback: 所有节点都同意时,才全部提交;只要一个节点拒绝就全部回滚。

XA 协议模型的组成部分:

  • 应用程序(AP): 实现业务功能,比如 MySQL 客户端程序;
  • 事务管理器(TM): 事务的全局调度者,协调各方提交或回滚,MySQL 客户端程序兼当事务管理器;
  • 资源管理器(RM): 数据库(MySQL)或消息中间件(MQ)实现 XA 接口函数,被 TM 调用;
    XA 定义了 TM/RM 间的接口规范(函数),TM 通过接口控制事务的开始和结束、提交和回滚等。

三阶段提交协议(3PC, Three Phase Commitment) 用于缓解两阶段提交的以下缺点(但不论2PC还是3PC,都应尽量避免分布式事务):

  • 单点问题: 事务管理器(TM)可能发生单点故障;
  • 同步阻塞: TM 协调各 AP/RM,期间处于阻塞状态,性能很低(可低至 10%)。

3.3 MySQL XA 相关说明

官方资料:XA Transactions
MySQL XA 分为两类:

  • 内部 XA: 用于同一数据库实例下跨多个引擎的事务,由 binlog 作为协调者;
  • 外部 XA: 用于跨多数据库实例的分布式事务,需要应用层介入作为协调者,下面指的是本情境;

早期 MySQL 版本对分布式事务的支持是有缺陷的,会话客户端异常断开时事务会丢失。
相关链接:Xa recovery and client disconnectionBinlogging XA-prepared transaction
直至 5.7.7 才稳定,现在的最新版本是 5.7.21(2018-01-14)。

3.4 MySQL XA 功能启用

  • 服务器参数必须开启 XA(默认为开启): innodb_support_xa=on
  • 存储引擎必须使用 InnoDB
  • 事务隔离级别必须设置为 SERIALIZABLE;

3.5 MySQL XA 操作步骤

MySQL> XA BEGIN|START 'trx'; ## 事务块开始
## DML 操作(INSERT/DELETE/UPDATE/SELECT)
MySQL> XA END 'trx'; ## 事务块结束
MySQL> XA PREPARE 'trx'; ## 准备事务并写入 binlog(若有 Slave 节点,binlog 也会被同步过去)
MySQL> XA RECOVER; ## 【可选】查看处于 PREPARE 阶段的 XA 事务
MySQL> XA COMMIT|ROLLBACK 'trx'; ## 事务提交或回滚(若有 Slave 节点,也同时提交)

4 Redis 分布式事务

分布式锁相关命令 SETNX key value 含义为 SET if Not eXists。
若 key 不存在,则设置并返回1(成功);若 key 已存在,则不作任何动作,返回 0(失败)。
Redis 为单进程单线程模式,使用队列将并发请求变成串行请求,保证了所有客户端不冲突。
使用 Redis 分布式锁的伪代码如下:

retval = <SETNX key value> ## 尝试占用
if (retval == 1) { ## 占用成功
    expire key timeout ## 以便本客户端异常中止时,该 KEY 会超时自动释放
    ## 主体操作
    delete key ## 释放锁
} else { ## 占用未遂    
}

5 RocketMQ 分布式事务

官方资料:RocketMQ Documentation
RocketMQ 中间件支持事务消息,可确保本地数据与MQ服务器上数据一致:

  • 发送一个准备消息,收到消息接口的回查地址;
  • 执行本地操作,执行结果可能是成功或者失败;
  • 确认消息发送,通过第一步得到的接口地址执行回查,并修改状态(根据本地事务的成功与否,修改状态为已提交或已回滚)。

6 JTA 事务

JDBC 事务局限于单个数据库,它在 JDBC Connection 中实现事务管理器。
而 JTA(Java Transaction API)提供了多个数据库(甚至包括其他资源如 MQ)的事务管理能力。

6.1 概述

官方资料:Java Transaction APIJSR 907
维基百科:Java事务API
JTA 规范定义了分布式事务中的事务管理器(TM)与资源管理器(RM)、应用程序(AP)等的 Java 接口。
JTA 是以 X/Open XA 体系结构为基础设计的。

6.2 API 组成

相关 Java 包有:javax.transactionjavax.transaction.xa

6.3 实例

SpringBoot 中使用 JTA 处理分布式事务(作者 Freud Kang)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值