分布式与一致性协议之MySQL XA协议

MySQL XA协议

概述

相信很多人都知道MySQL支持单机事务,那么在分布式系统中,涉及多个节点,MySQL又是怎样实现分布式事务的呢?
举个例子,一个业务系统需要接收来自外部的指令,然后访问多个内部其他系统来执行指令,但执行完指令后,需要同时更新多个内部MySQL数据库中的值(比如MySQL数据库A、B、C).由于业务敏感,所以系统必须处于一个一致性状态(也就是说,MySQL数据库A、B、C中的值要么同时更新成功,要么全部不更新),否则会出现有的系统显示指令执行成功,而有的系统显示指令尚未被执行的情况,导致多部门对指令执行结果理解混乱。
那么如何实现多个MySQL数据库更新的一致性呢?答案就是采用MySQL XA.

在我看来,MySQL通过支持XA规范的二阶段提交协议,不仅实现了多个MySQL数据库操作的事务,还能实现MySQL、Oracle、SQL Server等支持XA规范的数据库操作的事务。
通常,理解MySQL XA,不仅要能理解数据层分布式事务的原理,还要能在实际系统中更加深刻地理解二阶段提交协议,这样当你在实际工作中遇到多个MySQL数据库的事务需求时,你就知道如何通过MySQL XA来处理了。

老规矩,咱们先来看一道思考题。
假设有两个MySQL数据A、B(位于不同的服务器节点上),我们需要实现多个数据库更新(比如,UPDATE executed_table SET status=true WHERE id = 100)和插入操作(比如,INSERT INTO operation_table SET id = 100,op=‘get-cdn-log’)的事务,如图所示,那么在MySQL中如何实现呢?
MySQL是通过XA规范实现分布式事务的,所以我们有必要先来了解一下XA规范
在这里插入图片描述

什么是XA规范

提到XA规范,就不得不说DTP(Distributed Transaction Processing, 分布式事务处理)模型,因为XA规范约定的是DTP模型中的两个模块(事务管理其和资源管理器)的通信方式,如图所示。https://cdn.processon.com/userId2-663e15c8166b8c6ceb4340a7?e=1715348440&token=trhI0BY8QfVrIGn9nENop6JAc6l5nZuxhjQ62UfM:m5HMjar0byN2-MqjwQM3_VOZ1sA=

为了更好地理解DTP模型,我来解释下DTP各模块的作用。

  • 1.AP:应用程序(Application Program),一般是指事务的发起者(比如数据库客户端或者访问数据库的程序),定义事务对应的操作(比如更新操作
    UPDATE executed_table SET status = true WHERE id = 100)
  • 2.RM:资源管理器(Resource Manager),管理共享资源,并提供访问接口外部程序来访问共享资源,比如数据库。RM还应该具有事务提交或回滚的能力
  • 3.TM:事务管理器(Transaction Manager),一般指分布式事务的协调者。TM与每个RM进行通信,协调并完成事务的处理。

你是不是觉得这个模型看起来很复杂?其实在我看来,你可以这样理解DTP模型:应用程序访问、使用资源管器的资源,并通过事务管理器的事务接口(TX interface)定义需要执行的事务操作,然后事务管理器和资源管理器会基于XA规范执行二阶段提交协议。

那么XA规范是什么呢?它约定了事务管理器和资源管理器之间双向通信的接口规范,并实现了二阶段提交协议,如图所示。在这里插入图片描述

为了更好地理解这个过程,我们一起走一遍实现流程,以加深印象:

  • 1.AP(应用程序)联系TM(事务管理器)发起全局事务
  • 2.TM调用xa_open()建立与资源管理器的会话
  • 3.TM调用xa_start()标记事务分支(Transaction Branch)的开头
  • 4.AP访问RM(资源管理器)并定义具体事务分值的操作,比如更新一条数据记录(UPDATE executed_table SET status=true WHERE id = 100)和插入一条数据记录(INSERT INTO operation_table SET id =100, op=‘get-cdb-log’);
  • 5.TM调用xa_end()标记事务分支的结尾
  • 6.TM调用xa_prepare()通知RM做好事务分支提交的准备工作,比如锁定相关资源,也就是执行二阶段提交协议的提交执行阶段
  • 7.TM调用xa_commit()通知RM提交事务分支(xa_rollback()通知RM回滚事务),也就是执行二阶段提交协议的提交执行阶段
  • 8.TM调用xa_close()关闭与RM的会话。

整个过程也许有些复杂,不过你可以这样理解xa_start()和xa_end()在准备和标记事务分支的内容,然后调用xa_prepare()和xa_commit()(或者xa_rollback())执行二阶段提交协议,实现操作的原子性。注意,这些接口需要按照一定顺序执行,比如xa_start()必须在xa_end()之前执行。
另外,事务管理器对资源管理器调用的xa_start()和xa_end()这对组合,一般用于标记事务分支(就像上面的更新一条数据记录和插入一条数据记录)的开头和结尾。需要注意的是:

  • 1.对于同一个资源管理器,根据全局事务的要求,可以前后执行多个操作组合,比如,先标记一个插入操作,再标记一个更新操作;
  • 2.事务管理器只是标记事务,并不执行事务,最终是由应用程序通知资源管理器来执行事务操作的。

另外,XA规范还约定了如何向事务管理器注册和取消资源管理器的API接口(也就是ax_reg()和ax_unreg()接口)。这里需要注意的是,这两个接口是以ax_开头的,而不是像xa_start()那样以xa_开头,我们该如何通过MySQL XA实现分布式事务呢?

如何通过MySQL XA实现分布式事务

首先,你需要创建一个唯一的事务id(比如xid)来唯一标识事务,并调用XA START和XA END来定义事务分支对应的操作(比如
INSERT INTO operation_table SET id = 100, op = ‘get-cdn-log’),如图所示。在这里插入图片描述

接着,你需要调用XA PREPARE来执行二阶段提交协议的提交请求阶段,如图所示在这里插入图片描述

最后,你需要调用XA COMMIT 来提交事务(或者第哦啊用XA ROLLBACK来回滚事务),如图所示在这里插入图片描述
,至此,你就实现了全局事务的一致性
从上图所示的流程中可以看到,客户端在扮演事务管理器的角色,而MySQL数据库在扮演资源管理器的角色。但是这压力需要注意,上面流程中的xid必须是唯一值。
另外补充的是,如果你要开启MySQL的XA功能,则必须设置存储引擎为InnoDB,也就是说,在MySQL中,只有InnoDB引擎支持XA规范。d当然,可能有些人对MySQL XA有这样的疑问,能否将XA END和XA PREPARE合并到一起呢?答案是不能,因为在XA END之后,我们是可以直接执行XA COMMIT命令的,也就是一阶段提交(比如当共享资源变更只涉及一个RM时)。最后,我强调一下,MySQL XA性能不高,适合在并发性能要求不高的场景中使用,而我之所以需要采用MySQL XA实现分布式事务,是因为整个系统对并发性能要求不高,而且底层架构是多个第三方的,没办法改造。

注意

XA规范保证了全局事务的一致性,实现成本较低,而且得到了包括MySQL在内的主流数据库的支持。但是因为XA规范是基于二阶段提交协议实现的,所以它也存在二阶段提交协议的局限,列举如下:

  • 1.首先,XA规范存在单点问题,也就是说,因为事务管理器在整个流程中扮演的角色很关键,如果其宕机,比如第一阶段已经完成了,在第二阶段正准备提交的时候,事务管理器宕机了,那么相关的资源会被锁定,无法访问。
  • 2.其次,XA规范存在资源锁定的问题,也就是说,在进入准备阶段后,资源管理器中的资源将处于锁定状态,知道提交完成或者回滚完成才能解锁

思维拓展

虽然MySQL XA能解决数据库操作的一致性问题,但它的性能不高,适用于对并发性能要求不高的场景。那么,在MySQL XA不能满足并发需求是,我们应该如何重新涉及底层数据系统,来避免采用分布式事务呢?为什么呢?
当MySQL XA(即MySQL的分布式事务支持)无法满足并发性能需求时,可以考虑一下集中方法来重新设计底层数据系统,
以避免使用分布式事务并提高性能:

  • 1.业务逻辑分解
    1.1.将复杂的分布式事务分解为多个小的事务,每个事务在单个数据库上执行。通常在业务逻辑层面保证一致性,
    可以避免使用分布式事务
    1.2 使用最终一致性模型,如通过事件队列、发布/订阅机制等异步处理数据一致性问题

  • 2.数据分区。
    2.1.将数据水平分区到不同的数据库实例中,减少每个数据库实例上的并发访问压力
    2.2.通过分库分表来提高单个数据库实例的性能

  • 3.服务拆分
    3.1 采用微服务架构,将系统拆分为多个小的、松耦合的服务,每个服务对应一个数据库实例,从而减少分布式事务的使用
    3.2 各服务之间通过异步消息或者补偿事务来维持数据的一致性

  • 4.缓存机制
    4.1 使用缓存(如Redis)来减少对数据库的读写压力,提高并发性能
    4.2 通过缓存来处理读多写少的场景,减少对数据库的访问

  • 5.读写分离
    5.1 实施读写分离,将数据库的读操作和写操作分离到不同的数据库实例,以提高并发读取性能
    5.2 写操作仍然保证事务性,而读操作可以不参与事务,从而提高整体性能

  • 6.使用NoSQL数据库
    6.1 对于不需要强一致性的部分,可以考虑使用NoSQL数据库,如MongoDB、Cassandra等,它们通常提供更高的并发性能

  • 7.业务流程优化
    7.1 优化业务流程,减少事务性操作的一来。例如,通过业务上的妥协,允许一定程度的最终一致性

  • 8.性能优化
    8.1 对现有数据库进行性能优化,包括但不限于索引优化、查询优化、硬件升级等

  • 9.避免长事务
    9.1 长事务会占用大量资源并可能导致锁定问题,通过涉及更短的事务来减少对系统资源的占用

  • 10.分布式数据库
    10.1 考虑使用分布式数据库系统,如Google Spanner 、CockroachDB等,它们在涉及时就考虑了分布式事务的性能

避免使用分布式事务的原因主要包括:

  • 1.性能开销:分布式事务通常涉及多个节点,需要额外的通信和协调,这回增加延迟并降低性能
  • 2.复杂性:分布式事务的管理和调试更加复杂,可能导致难以追踪的问题
  • 3.可用性问题:在分布式系统中,任何节点的故障都可能影响到整个事务,降低了系统的可用性。
    因此,在涉及底层数据系统时,应根据业务需求和系统特点,选择合适的策略来避免分布式事务,同时确保系统的高性能和高可用性

重点总结

  • 1.XA规范是个标准的规范,也就是说,无论是否相同的数据库,只要这些数据库(比如MySQL、Oracle、SQL Server)支持XA规范,那么它们就能实现分布式事务,也就是能保证全局事务的一致性
  • 2.相比商业数据库对XA规范的支持,MySQL XA性能不高,所以,我不推荐在高并发的性能至上的场景中使用MySQL XA.
  • 3.在实际开发中,为了降低单点压力,我们通常会根据业务情况进行分库分表,即将表分布在不同的库中,那么在这种情况下,如果后续需要保证全局事务的一致性,则也需要实现分布式事务。

虽然MySQL XA能实现数据层的分布式事务,但会面临这样一个问题:在接收到外部的指令后,需要访问多个内部系统,执行指令约定的操作,而且,必须保证指令执行的原子性,也就是说,要么全部成功,要么全部失败,此时应该怎么做呢?答案是TCC

  • 32
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coffee_babe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值