尹博学
蚂蚁金服资深技术专家
尹博学, 蚂蚁金服资深技术专家, 目前负责数据中间件技术方向. 此前在百度负责数据库内核及集群技术方向. 在分布式事务, 数据库高性能 / 高可靠架构, 数据库内核等领域有较为深入的研究和丰富的工程实践.
随着互联网技术快速发展, 数据规模增大, 分布式系统越来越普及, 采用分布式数据库或者跨多个数据库的应用在中大规模企业普遍存在, 而由于网络, 机器等不可靠因素, 数据不一致问题很容易出现.
而对金融业务来说, 它们面向互联网进行变革时, 除了一致性要求外, 对高可用, 高可扩展性也有极高的要求. 这是金融级分布式系统的最大挑战之一.
在蚂蚁金服核心系统提出微服务化时, 曾遇到了非常大的技术难题. 首先是在服务拆分以后, 面临跨服务的一致性问题; 其次, 支付宝当时的峰值交易量已经非常高了, 在解决一致性问题的同时, 还需要兼顾性能.
然而, 在当时最常见的还是基于 XA 协议的分布式事务解决方案, 虽然该方案能够实现跨服务一致性, 但是在热点数据的处理上, 却不能满足性能需求.
因此, 蚂蚁金服微服务化过程中急需一种即能保证一致性, 又能保证高性能的方案. 当时经过一系列调研和讨论, 最终选择了以 BASE 最终一致性思想为基础, 在业务层实现两阶段提交的 TCC 分布式事务解决方案, 该方案既能保证跨服务的最终一致, 又能通过业务灵活加锁的方式大幅减少资源层加锁时间, 高效处理热点问题.
随着蚂蚁金服业务不断丰富, 业务逻辑越来越复杂, 同时蚂蚁金融云上客户也越来越多, 他们对分布式事务解决方案也不只是追求极限性能, 也对接入便捷性, 实时一致性有了要求.
近日于北京举行的 2018 DTCC 中国数据库技术大会上, 蚂蚁金服数据中间件负责人尹博学分享了蚂蚁金服在分布式事务技术方向上全系列技术方案与产品: TCC,FMT,XA 三种各有侧重各有优势的方案. 同时也为大家揭示, 在这些方案背后的 "技术秘密".
一. 数据一致性问题
1.1 支付宝 SOA 架构演进
最初的支付宝系统架构非常简单, 是一个典型的 web 系统, 交易, 支付, 账务系统是没有分开的, 全部集中在一个大工程里, 底层用 DB 存储, Web 服务使用 Spring 等框架开发, 最上层使用 LB 转发用户流量.
随着代码规模和业务逻辑复杂度的增长, 将所有的业务模块放在一个 Web 服务内的缺点日益凸显. 因此推动了服务模块化拆分, 演进成 SOA 架构, 很好地解决了应用的伸缩性问题.
在这个架构中, 交易系统, 支付系统, 账务系统分别独立出来了, 它们之间的调用靠 RPC, 除了服务模块化拆分, 还有数据库的垂直拆分, 根据业务系统功能, 将数据库拆分为多个, 每个系统使用独立的数据库.
1.2 数据一致性问题
数据垂直拆分后, 减少了单库的性能瓶颈, 但也带来了数据一致性的问题. 如下图所示:
一笔转账业务需要跨交易, 支付, 账务三个系统才能完成整个流程, 并且要求这三个系统要么一起成功, 要么一起失败. 如果有的成功, 有的失败, 就有可能造成数据不一致, 那么这个业务行为肯定不会被用户所理解. 所以, 我们系统面临的问题就是 SOA 架构下的数据一致性.
二. 金融级一致性要求与海量并发处理能力
考虑到在解决一致性问题的同时, 还要兼顾海量并发处理能力, 蚂蚁金服内部结合 BASE 理论的思想, 选择在业务层实现 2PC(两阶段事务提交) 的方式来解决该问题.
BASE 理论是指 BA(Basic Availability, 基本业务可用性);S(Soft state, 柔性状态);E(Eventual consistency, 最终一致性). 该理论认为为了可用性, 性能与降级服务的需要, 可以适当降低一点一致性的要求, 即 "基本可用, 最终一致".
一般来讲, 在传统的单机数据库或者集中式存储数据库里, 对于一致性的要求是实时一致; 而对于分布式系统, 服务与服务之间已经实现了功能的划分, 逻辑的解耦, 也就更容易弱化一致性, 允许处理过程中, 数据的短暂不一致, 只需要保证数据最终达到一致.(BASE 理论的分析可以参考本系列文章的第二篇).
2.1 TCC(Try-Confirm-Cancel) 模型
TCC 分布式事务模型包括三部分, 如下所示
. 主业务服务: 主业务服务为整个业务活动的发起方, 服务的编排者, 负责发起并完成整个业务活动.
. 业务服务: 从业务服务是整个业务活动的参与方, 负责提供 TCC 业务操作供主业务服务调用
a) 初步操作 Try: 完成所有业务检查, 预留必须的业务资源.
b) 确认操作 Confirm: 真正执行的业务逻辑, 不作任何业务检查, 只使用 Try 阶段预留的业务资源. 因此, 只要 Try 操作成功, Confirm 必须能成功. 另外, Confirm 操作需满足幂等性, 保证一笔分布式事务有且只能成功一次.
c) 取消操作 Cancel: 释放 Try 阶段预留的业务资源. 同样的, Cancel 操作也需要满足幂等性.
. 业务活动管理器: 业务活动管理器管理控制整个业务活动, 包括记录维护 TCC 全局事务的事务状态和每个从业务服务的子事务状态, 并在业务活动提交时调用所有从业务服务的 Confirm 操作, 在业务活动取消时调用所有从业务服务的 Cancel 操作.
根据统计, 2017 年, DTX 在支付宝内部, 接入 DTX 的系统超过 100+, 支持场景涉及支付, 转账, 理财, 保险等, 双十一支持了峰值 25.6 万 TPS 的支付请求, 每天处理资金以千亿计, 确保了资金的绝对安全.
2.1.1 TCC 与 XA 对比 --- 并发性优势
TCC 把两阶段拆分成了两个独立的阶段, 通过资源业务锁定的方式进行关联. 资源业务锁定方式的好处在于, 既不会阻塞其他事务在第一阶段对于相同资源的继续使用, 也不会影响本事务第二阶段的正确执行.
XA 模型的并发事务
TCC 模型的并发事务
从上面的对比可以发现, TCC 模型相比 XA 模型进一步减少了资源锁的持有时间. XA 模型下, 在 Prepare 阶段是不会把事务 1 所持有的锁资源释放掉的, 如果事务 2 和事务 1 争抢同一个资源, 事务 2 必须等事务 1 结束之后才能使用该资源.
而在 TCC 里, 因为事务 1 在 Try 阶段已经提交了, 那么事务 2 可以获得互斥资源, 不必再等待事务 1 的 Confirm 或 Cancel 阶段执行完. 也就是说, 事务 2 的 Try 阶段可以和事务 1 的 Confirm 或 Cancel 阶段并行执行, 从而获得了一个比较大的并发性能提升.
2.2 极致性能优化
2.2.1 双十一业务量指数级增长的挑战
2010 年, 阿里开始做大促, 每年双十一的零点峰值成 3 倍以上速度增长, 如下图所示:
折线上的点代表着每一年的交易峰值 (TPS). 前面讲过, 每次分布式事务需要协调多个参与方, 因此, 每年交易峰值乘以一个倍数才是分布式事务框架要协调最终达到一致性状态的峰值分支事务数量. 可以看到, 去年的时候已经达到每秒百万级水平.
其实, 2013 年是对我们挑战很大的一年, 2013 年双 11 提出的峰值目标, 按照当时大促准备的计算和存储资源, 是无法完成的, 怎么办?
只能选择继续优化事务框架, 让它尽量少消耗资源, 并去获得一个更大的性能提升. 我们根据 TCC 模型的特点以及日常开发时积累的经验, 针对性能问题, 做了以下两类优化.
2.2.2 极致性能优化之同库模式
之前的业务活动管理器是一个单独的服务, 每次启动业务活动, 登记业务操作, 提交或回滚业务活动等操作都需要与业务活动管理器交互, 并且交互次数与参与者个数成正相关.
因此, 为了追求极限性能, 将业务活动管理器的远程存储替换成本地存储, 如下所示:
减少了 RPC 的调用, 同时还会做一些减少存储次数的优化, 从而获得性能收益.
通过这种方式, 减少 RPC 调用耗时, 大幅降低事务执行时间. 同时还针对支付, 账务等访问频繁的特殊从业务服务, 优化处理过程, 不再创建单独的分支事务记录, 而是将这个信息与主事务记录合并, 在创建主事务记录的同时, 记录分支事务, 最大程度减少与数据库的交互次数, 从而获得更高的性能收益.
下面是同库模式优化前后的时序图对比:
优化前时序图
优化后时序图
其中, 绿色方块表示本地数据库访问. 可以发现, 优化后减少了:(2+n) 次 PRC 延迟 + 1 次数据库访问延迟 (n 表示参与者个数).
2.2.3 极致性能优化之异步化
从理论上来说, 只要业务允许, 事务的第二阶段什么时候执行都可以, 因此资源已经被业务锁定, 不会有其他事务动用该事务锁定的资源. 如下图所示:
这就是 TCC 分布式事务模型的二阶段异步化功能, 各从业务服务的第一阶段执行成功以后, 主业务服务就可以提交完成, 框架会保证正确记录事务状态, 然后再由框架异步的执行各从业务服务的第二阶段, 从而比较完整的诠释最终一致性.
大促的尖峰时刻是从零点开始的几十秒或一分钟之内, 在这个时刻的交易, 我们会把二阶段的操作从同步转成异步, 在冲高的那一刻, 二阶段就停止了, 一阶段正常扣款, 等着交易零点三十分或者夜里一点开始回落的时候, 我们才开始打开二阶段, 集中做二阶段的动作.
优化后, Confirm 阶段在空闲时段异步执行:
1. 假设 Confirm 阶段与 Try 阶段耗时相同, 单个事务耗时减少 50%
2. 数据库资源消耗减少 50%
三. 金融云场景下接入便捷性需求强烈
3.1 金融云元年, 全金融场景覆盖
众所周知, 蚂蚁金服在近几年除了支付业务以外, 还发展出了很多复杂的金融业务, 比如财富, 保险, 银行等. 同时, 在 2014 年蚂蚁金服全面开启了金融云时代, 也会对外赋能合作方和客户. 在这些新的场景下面, 分布式事务产品面临新的挑战, 客户的性能需求可能不像蚂蚁金服内部要求那么高, 而是更关注接入的便利性和通用性, 要求简单易用, 对业务代码无侵入. 因此, 分布式事务产品开始全面升级, 满足更多客户对云端产品的需求.
3.2 FMT(Framework-managed transactions) 模型
TCC 模型作用于业务层, 负责协调从业务服务的最终一致性. 所有被纳入到分布式事务的从业务服务, 需要为框架提供 Try,Confirm,Cancel 三个方法, 并且需要满足幂等性. 由于方法实现和要满足的约束条件都需要业务方提供, 这无疑就大大提高了接入门槛. 所以我们在 TCC 模型上继续往前推进发展, 提出了 FMT 模型来解决接入便捷性的问题.
FMT 模型, 这是蚂蚁金服分布式事务产品的可托管版本, 让用户可以基本无侵入的方式下解决分布式事务问题.
FMT 分布式事务模型与 TCC 模型类似, 也同样包含主业务服务, 从业务服务, 业务活动管理器, 如下所示:
不同的是, 从业务服务不再需要提供 Try,Confirm,Cancel 三个方法, 而是直接按照 JDBC 标准, 通过托管框架与底层数据库交互, 就像使用普通数据源一样. 托管框架对业务来说是透明的, 主要负责解析 SQL 语义, 自动生成两阶段操作.
3.2.1 FMT 模型实现原理
托管框架的两阶段操作如下图所示:
FMT 框架要求从业务服务只需要正常执行 SQL 操作就可以了, 框架会把业务的本地事务操作作为第一阶段. 在第一阶段, 框架会拦截用户 SQL, 解析 SQL 语义, 然后把业务 SQL 涉及数据执行前后的状态保存下来, 即数据快照, 这个就相当于在逻辑上完成了数据库内的 undo 和 redo 操作. 在第二阶段, 如果这个事务要提交, 那么框架直接把快照数据删除就可以了, 因为第一阶段的正常操作已经执行完成. 如果该事务要回滚, 那么会先校验脏写, 根据第一阶段保存的执行后的快照, 检查在本事务执行过程中, 数据有没有被其他操作修改, 如果没有, 则把数据执行前的快照拿出来, 完成回滚操作.
3.2.1.1 一阶段示例
举个例子, 上图左边这张表有两列, 一列是账号, 另一列是金额. 这时如果要针对该账户执行一条 update 操作, 框架会怎么做呢?
在 update 之前, 会先把账户的金额保存下来, 执行 update 操作, 然后把执行之后的金额保存下来. 因为在二阶段有可能会是回滚操作, 回滚的时候如果想把执行之前的数据覆盖回去的话, 必须要保证在覆盖的那个时刻, 这些行上面的数据没有被别人变更过, 所以最后会加一个逻辑行锁, 这个就是金融系统的特性需求.
3.2.2 与数据访问代理集成
为了更加简化云上用户接入, 我们继续和内部产品数据访问代理 DBP 合作集成, 如下所示:
分布式事务产品框架可以认为是被集成在数据访问代理里, 当进行一个事务时, 上层业务方对于底下的分布式事务和本地事务是一视同仁的, 通过数据代理看一个事务, 并执行 SQL. 如果是分布式事务, 数据访问代理会通知框架去执行前面提到的一系列保证事务的操作, 以保证数据的最终一致.
四. 数据实时一致性, 通用性, 性能
4.1 XA 模型
2018 年蚂蚁金服推出第三代分布式事务解决方案, 即加入 XA 模式的解决方案, 全面支持标准 XA 协议. 该模式覆盖面广, 可无缝接入支持 XA 的数据库, 消息等, 与自研数据库 OceanBase 共同形成实时数据一致性的整体解决方案, 极大地降低传统业务上云的迁移成本.
事实上, TCC 和 FMT 两个模型都是在最佳实践上追求数据的最终一致性, 而不是实时一致性.
我们分析了金融云上的客户发现, 如果把业务模型假定成数据最终一致性, 那么依然有很多金融客户不得不做出很大的妥协和变更, 尤其是原有的业务组织模型和业务逻辑实现. 而且这种妥协和调整的工作量是很大的, 门槛也是非常高的. 同时这些客户的场景对热点性能要求没有那么严苛.
所以我们基于标准 XA 做了一个 XA 模型来满足客户对数据实时一致性的需求.
原生 XA 协议提出至今, 大概有 10-20 的时间了, 但是在工业界应用的历史和案子都很少. 为什么会这样呢? 我们认为最重要的一点就是在追求数据实时一致性的同时, 性能损失太大了. 主要有两个比较方面的性能损失, 一个是读和写之间的冲突, 另一个是写与写之间的冲突.
4.2 标准 XA 问题分析
了解数据库内核的人都清楚, 数据库内部解决写和非加锁读的冲突是通过 MVCC 机制来实现的. 假如说最新的数据块在更新的同时, 你的读是可以读正在更新的数据块的上一个快照. 但是在分布式架构下, 单机 MVCC 机制并不能满足数据实时性一致性要求.
依然是转账业务场景, A 账户给 B 账务转账 10 块钱. 但是 A 账户和 B 账户分别在两个数据库分片 DB1 和 DB2 上. 其操作执行过程如下所以:
如上图所示, DB1 的本地子事务已经提交完毕, 但是 DB2 的本地子事务还没提交, 这个时候只能读到 DB1 上子事务执行的内容, 读不到 DB2 上的子事务. 也就是说, 虽然在单个 DB 上的本地事务是实时一致的, 但是从全局来看, 一个全局事务执行过程的中间状态被观察到了, 全局实时一致性就被破坏了.
但是原生的 XA 协议没有规定快照读这个概念, 也就没有定义怎么实现全局实时一致性. 最简单的做法就是使用串行化的隔离级别, 即使是快照读也需要转换为加锁读, 从而来保证分布式事务的实时一致性.
当然, 由于串行化隔离级别的性能较差, 很多分布式数据库都自己实现了分布式 MVCC 机制来提供全局的实时一致性读. 一个基本思路是用一个集中式或者逻辑上单调递增的东西来控制生成全局快照 (Snapshot), 每个事务或者每条 SQL 执行时都去获取一次, 从而实现不同隔离级别下的全局一致性, 如下图所示:
在 DB1 的本地子事务已经提交完毕, DB2 的本地子事务还没提交的中间状态, 其他并发事务只能看到该事务执行之前的快照.
我们的分布式事务产品同样实现了分布式 MVCC 机制, 从而在保证实时一致性的同时, 最大程度的保证读写并发性能.
4.3 并发写优化 --- 与 OB 深度定制 commit 延迟优化
除了实现分布式 MVCC 保证并发读写性能外, 我们还与自研数据库 OceanBase 深度定制优化并发写, 进一步提升产品性能, 共同打造实时数据一致性的整体解决方案.
传统标准的二阶段提交过程如下:
其中, 绿色方块表示持久化日志, 黄色方块表示事务提交. 从图中可以看到, 单次 Commit 操作需要有 3 次日志延迟, 1 次事务延迟以及 2 次 RPC 延迟.
OceanBase 内部实现 XA 协议的时候, 会在和协调者交互的时候附带一些信息, 并且在 Commit 时落盘, 减少 Commit 过程中涉及到的 RPC 和落盘的操作, 以达到减少用户 Commit 时间的效果.
优化后时序图如下:
虽然在 Commit 操作之后, 还有 Clear 操作, 但是在执行 Clear 时, 用户的 Commit 请求已经返回了, 所以并不影响用户感知的 Commit 请求延迟. 因此, 从用户感知的角度来说, 单次 Commit 操作实际上只需要 1 次日志延迟, 1 次事务延迟 以及 2 次 RPC 延迟.
通过以上优化, 两阶段提交与普通提交的落盘次数和 RPC 次数是相同的, 也就是说耗时和普通提交相差无几, 写和写之间的冲突所带来的额外性能消耗将被降低很大一部分.
五. 总结
本文介绍了蚂蚁金服在分布式事务上, 经过多年发展, 服务于内外部大量不同业务, 沉淀出的一整套包含 TCC,FMT,XA 模型的分布式事务解决方案.
首先, 为了兼顾海量并发处理能力与一致性的问题, 我们基于 BASE 思想, 在业务层实现了 TCC 模型, 并且优化了其工程实践, 让它的性能可以达到比业界其它产品高很多的状态.
其次, 因为上层业务系统的复杂, 业务种类的丰富等等业务需求, 对分布式事务解决方案提出了全新的要求, 所以我们在 TCC 模型基础上又加入了 FMT 模型, 其简单易用, 对业务无侵入的特点, 可以较好的解决金融云场景下接入便捷性问题.
最后, 我们对数据实时一致性, 通用性, 性能再思考, 与自研数据库 OceanBase 深度定制, 推出 XA 模型, 共同打造实时数据一致性的整体分布式事务解决方案.
来源: http://www.92to.com/bangong/2018/05-24/33835129.html