技术解读 | PolarDB-X 分布式事务的实现

本文涉及的产品
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
云数据库 RDS SQL Server,基础系列 2核4GB
简介: PolarDB-X 采用的是基于 XA 协议实现的分布式事务策略,本文会重点介绍 XA 事务和 TSO 事务。

作者:光气

背景

ACID 事务是关系型数据库一个重要的特性,也是 NewSQL 数据库最大的挑战之一。在 PolarDB-X 的架构中,Data Node(DN)是通过 Paxos 同步日志的,保证了事务的持久性(Durability),而原子性(Atomicity)、一致性(Consistency)和隔离性(Isolation)需要通过合适的事务策略来保证。除此之外,在分布式场景下,由于数据被分散在不同节点,线性一致性(linearizability)也是事务策略的重要特性。

目前主流的分布式数据库都使用了基于两阶段提交(2PC)的策略,包括 Percolator 算法和 XA 协议。Percolator 是 Google 基于 BigTable 做的分布式系统,一个重要的设计就是使用了 Percolator 算法支持分布式事务。在 Percolator 算法中,最重要的参与者是客户端,更新被缓存在客户端,并在 Commit 时通过 2PC 提交到服务端。Percolator 算法的最大优势之一是主要状态都在客户端完成,服务端只需要支持简单的 CAS 就可以,不需要维护事务状态,也不需要引入额外的事务管理器。但 Percolator 也有一些已知缺陷:提交阶段延迟较高,仅支持乐观锁场景、冲突错误只能在提交时汇报等。XA 协议全名为 X/Open XA 协议,是一项通用的事务接口标准。XA 协议也基于两阶段提交的策略。

PolarDB-X 的事务策略

PolarDB-X 采用的是基于 XA 协议实现的分布式事务策略,其中又分为多种实现:
• 适用于 MySQL 5.6 的 BestEffort 事务
基于 InnoDB 的 XA 事务
PolarDB-X 2.0 中实现的 TSO 事务

本文会重点介绍 XA 事务和 TSO 事务。

XA 事务

在 XA 协议的设计中,有两种角色:
• 事务管理器(Transaction Manager,TM):负责发起事务的提交,失败时处理事务异常,在 PolarDB-X 中这个角色由计算节点(CN) 承担
• 资源管理器(Resource Manger,RM):事务的参与方,如 MySQL 中的一个库,在 PolarDB-X 中这个角色由存储节点(DN)承担

XA 协议由 TM 向多个 RM 通过 XA PREPARE 和 XA COMMIT 两条命令完成两阶段提交。两阶段提交常常为人诟病的问题是 TM 的单点问题和 Commit 阶段发生异常可能导致的数据不一致问题。为此,在 PolarDB-X 的实现中,我们额外引入了事务日志表以及 COMMIT POINT 的概念。确认所有参与节点 Prepare 成功的情况下,我们向全局事务日志添加一条事务提交记录作为 COMMIT POINT。在 TM 发生异常的情况下,我们可以选择新的 TM 继续完成两阶段提交,新 TM 会根据主库中是否存在 COMMIT POINT 记录选择恢复事务状态或者回滚事务。

• 如果 COMMIT POINT 不存在,那么可以保证没有任何一个 RM 进入 Commit Phase,此时可以安全回滚所有 RM。
• 如果 COMMIT POINT 存在,那么可以保证所有 RM 都已经完成了 Prepare Phase,此时可以继续进行 Commit Phase。

4.png

XA 事务在并发行上有一定的局限性,在执行的过程中,我们必须使用类似 Spanner 锁定读写的事务策略,为所有 SELECT 操作带上 LOCK IN SHARE MODE ,造成读写相互阻塞。基于Lock模式的分布式事务在有单记录并发冲突时整体性能偏低,目前数据库业界的解法是事务多版本的MVCC策略,尽管我们的 DN 节点 InnoDB 本身支持基于 MVCC 的快照读,但我们却无法提供高效的快照读事务策略,原因是事务提交时不同 DN 执行 XA COMMIT 的时间不同,一个快照读请求请求不同分片的时间也不同,这意味着发起一个跨 DN 的快照读请求时,可能读到一个事务的部分已提交数据,无法得到全局的一致性视图(Snapshot)。

TSO 事务

造成 XA 事务无法提供快照读功能的核心原因是我们缺乏一个全局时钟来排序每个事务,每个事务和视图在不同的 DN 上可能是不同的顺序。因此在 PolarDB-X 2.0 中,我们引入了基于 XA 事务优化的 TSO 事务。TSO 事务需要一个生成全局单调递增 Timestamp 的策略,常见的策略有 True Time(Google Spanner)、HLC(CockroachDB)、TSO(TiDB),在我们目前的实现中,我们使用了 TSO 策略,由 GMS(元数据管理服务)作为一个高可用的单点服务承担生成 Timestamp 的任务。TSO 保证了正确的线性一致性和良好的性能,只是在跨全球机房部署的场景会带来较高的延迟。

5.png

原生的 InnoDB 引擎无法满足我们支持 TSO 事务的需求,因此我们修改了 InnoDB 引擎的提交逻辑和可见性判断逻辑,在 XA BEGIN 和 XA COMMIT 前插入了我们自定义的两个变量 SNAPSHOT_TS 和 COMMIT_TS。

• SNAPSHOT_TS 用于判断其他事务提交数据对当前事务的可见性,统一了一个分布式事务在每个分片上进行读取发生的时间。SNAPSHOT_TS 决定了当前事务的快照。
• COMMIT_TS 统一了一个分布式事务在每个分片上提交数据发生的时间,会被记录到 InnoDB 引擎中。COMMIT_TS 决定了当前事务在全局事务中的顺序。

在私有化的 InnoDB 中,我们会根据事务的 SNAPSHOT_TS 来决定可见性,同时我们也会让新事务的读请求在遇到处于 prepare 状态的数据时进行等待,避免处于 prepare 状态的事务 COMMIT_TS 比当前 SNAPSHOT_TS 更小导致 commit 前后数据不同的问题。

基于 TSO 事务,我们也做了一系列新功能和优化:

备库一致性读
由于 TSO 具备了全局快照的能力,我们可以将查询的任何一部分转发到任何分片的任何备份节点上进行读取,并不破坏事务语义。这个 feature 对我们实现面向 HTAP 的混合执行器非常重要,TP/AP 的资源分离以及 MPP 执行框架都基于这个 feature 来保证正确性。

一阶段提交优化
如果在提交阶段我们发现事务只涉及了一个分片,那么我们就会将其优化为一阶段提交,使用 XA COMMIT ONE PHASE 语句提交事务。对于正常的 TSO 事务,我们取了 SNAPSHOT_TS 和 COMMIT_TS 两个时间戳,而对于一阶段提交的事务来说,其实行为与单机事务类似,因此我们并不需要通过 TSO 获取 COMMIT_TS,而是可以直接由 InnoDB 计算出一个合适的 COMMIT_TS 来提交事务。具体的计算规则是:COMMIT_TS = MAX_SEQUENCE + 1,其中 MAX_SEQUENCE 为 InnoDB 本地维护的历史最大的 snapshot_ts。

只读连接优化
一个事务如果使用 START TRANSACTION READ ONLY 开启,那么我们就会将事务标记为只读事务。我们会直接通过多个 autocommit 的单语句获取需要的数据,避免长期持有连接和事务的开销。由于 TSO 的存在,我们只需要使用相同的 TS 就能保证读到相同的数据,因此我们通过私有协议支持在每个语句内置一个 SNAPSHOT_TS,保证了同一个事务内的多条单语句读到相同的数据。

通常,用户很少会主动使用 START TRANSACTION READ ONLY 开启事务,因此对于常规事务,我们也针对每个连接使用了延迟开启 XA 事务的策略。对于所有连接,默认以只读的形式不开启事务,直到第一个写请求或者 FOR UPDATE 读请求再进行正常的 XA 事务流程。

除了只读事务,这个优化的另一个场景是多分片读单分片写的事务,通过这个方案我们能将其优化为一阶段提交事务,在 TPCC 的测试结果上得到了 14% 的性能提升。

当前读事务优化
在上面的只读连接优化中,我们通过将仅使用快照读的连接摘出事务之外,来优化 COMMIT_TS 的获取。而如果是完全相反的情况 ——— 所有连接都是写操作或者带锁的当前读操作,那么我们完全不需要进行快照读。因此我们也做了这样一个优化:仅在第一次进行快照读时获取 SNAPSHOT_TS。这个优化针对的是一些对 Serializable 有很强需求的场景:

BEGIN;
SELECT balance FROM accounts WHERE id = 0 FOR UPDATE; # 检查余额,需要加锁
UPDATE accounts SET balance = balance - 1 WHERE id = 0;
UPDATE accounts SET balance = balance + 1 WHERE id = 1;
COMMIT;

上面的 SQL 执行的是一个典型的转账场景,将 1 元从 id 为 0 的账户转到 id 为 1 的账户,在整个事务中都没有使用到快照读,因此针对这种场景我们会省略 SNAPSHOT_TS 的获取。

单机多分片优化
如果分布式事务的多个分区位于同一个 DN 节点上时,实际上可以将它们共同视作一个单机事务。这种情况下,可以将多个 RPC 合并,一次性地完成多个分片的 PREPARE 和 COMMIT。如果所有分片均在同一个 DN 节点上,甚至可以将事务以 1PC 的方式提交。

异步提交优化
上述优化针对的依然是一部分特定场景,对于多分片的分布式事务,往往还是相比单机事务有较长的延迟。因此我们设计了异步提交方案,针对任何分布式事务,都可以在完成了 PREPARE 阶段后直接返回成功,达到接近单机事务的提交延迟(一次跨机房 RPC)且不影响数据可靠性和线性一致性。我们会在之后的文章中详细介绍这一方案,欢迎大家关注我们专栏。

总结

本文主要介绍了 PolarDB-X 2.0 中的 TSO 分布式事务的实现。相比于 PolarDB-X 1.0 (DRDS)默认的 XA 事务,TSO 事务性能更优:通过 MVCC 方式避免加读锁,同时通过一系列优化(例如异步提交)提升了性能。此外借助 TSO 事务还提供了备库一致性读的能力。


【相关阅读】
PolarDB-X 一致性共识协议 (X-Paxos)
技术解读 | PolarDB-X 强一致分布式事务

相关实践学习
快速体验PolarDB开源数据库
本实验环境已内置PostgreSQL数据库以及PolarDB开源数据库:PolarDB PostgreSQL版和PolarDB分布式版,支持一键拉起使用,方便各位开发者学习使用。
目录
相关文章
|
3月前
|
存储 NoSQL 关系型数据库
非关系型数据库-MongoDB技术(二)
非关系型数据库-MongoDB技术(二)
|
3月前
|
NoSQL 关系型数据库 MongoDB
非关系型数据库-MongoDB技术(一)
非关系型数据库-MongoDB技术(一)
|
1月前
|
监控 算法 网络协议
|
2月前
|
Cloud Native 关系型数据库 分布式数据库
|
2月前
|
人工智能 文字识别 Java
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
尼恩,一位拥有20年架构经验的老架构师,通过其深厚的架构功力,成功指导了一位9年经验的网易工程师转型为大模型架构师,薪资逆涨50%,年薪近80W。尼恩的指导不仅帮助这位工程师在一年内成为大模型架构师,还让他管理起了10人团队,产品成功应用于多家大中型企业。尼恩因此决定编写《LLM大模型学习圣经》系列,帮助更多人掌握大模型架构,实现职业跃迁。该系列包括《从0到1吃透Transformer技术底座》、《从0到1精通RAG架构》等,旨在系统化、体系化地讲解大模型技术,助力读者实现“offer直提”。此外,尼恩还分享了多个技术圣经,如《NIO圣经》、《Docker圣经》等,帮助读者深入理解核心技术。
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
|
2月前
|
关系型数据库 分布式数据库 数据库
PolarDB 开源:推动数据库技术新变革
在数字化时代,数据成为核心资产,数据库的性能和可靠性至关重要。阿里云的PolarDB作为新一代云原生数据库,凭借卓越性能和创新技术脱颖而出。其开源不仅让开发者深入了解内部架构,还促进了数据库生态共建,提升了稳定性与可靠性。PolarDB采用云原生架构,支持快速弹性扩展和高并发访问,具备强大的事务处理能力及数据一致性保证,并且与多种应用无缝兼容。开源PolarDB为国内数据库产业注入新活力,打破国外垄断,推动国产数据库崛起,降低企业成本与风险。未来,PolarDB将在生态建设中持续壮大,助力企业数字化转型。
96 2
|
3月前
|
人工智能 Kubernetes Cloud Native
深度对话 解锁阿里云分布式云原生技术落地新姿势
深度对话 解锁阿里云分布式云原生技术落地新姿势
深度对话 解锁阿里云分布式云原生技术落地新姿势
|
3月前
|
Cloud Native 关系型数据库 分布式数据库
PolarDB开源项目未来展望:技术趋势与社区发展方向
【9月更文挑战第5天】随着云计算技术的发展,阿里云推出的云原生分布式数据库PolarDB受到广泛关注。本文探讨PolarDB的未来展望,包括云原生与容器化集成、HTAP及实时分析能力提升、智能化运维与自动化管理等技术趋势;并通过加强全球开源社区合作、拓展行业解决方案及完善开发者生态等措施推动社区发展,目标成为全球领先的云原生数据库之一,为企业提供高效、可靠的服务。
105 5
|
4月前
|
C# UED 定位技术
WPF控件大全:初学者必读,掌握控件使用技巧,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,控件是实现用户界面交互的关键元素。WPF提供了丰富的控件库,包括基础控件(如`Button`、`TextBox`)、布局控件(如`StackPanel`、`Grid`)、数据绑定控件(如`ListBox`、`DataGrid`)等。本文将介绍这些控件的基本分类及使用技巧,并通过示例代码展示如何在项目中应用。合理选择控件并利用布局控件和数据绑定功能,可以提升用户体验和程序性能。
70 0
|
2月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?

相关产品

  • 云原生分布式数据库 PolarDB-X
  • 云原生数据库 PolarDB