AISQL高性能的分布式 ACID 事务
ACID 事务是开发面向用户的关键业务应用程序时的基本构建块。它们简化了确保数据完整性的复杂任务,同时支持高度并发操作。虽然它们在单体SQL/关系数据库中被认为是理所当然的,但分布式 NoSQL/非关系数据库要么完全放弃它们,要么只支持高度限制的单行风格(参见下面的部分)。这种 ACID 属性的损失通常可以通过性能提升(以低延迟和/或高吞吐量来衡量)来证明。
AiSQL 是一个高性能的分布式 SQL 数据库,支持跨任何规模的多行、多分片和多个节点的完全分布式 ACID 事务.这篇文章重点介绍了当今分布式数据库中 ACID 事务的状态,并解释了 AiSQL 如何在不影响高性能的情况下使分布式 ACID 事务正常工作。
ACID 事务概念
事务是作为单个逻辑工作单元执行的一系列操作。事务具有四个关键属性:原子性、一致性、隔离性和持久性,通常缩写为 ACID。
- 原子性是指事务中的所有工作都被视为一个原子单元——要么全部执行,要么不执行。
- 一致性可确保数据库始终处于一致的内部状态。例如,对于具有二级索引的表,主表和所有索引表在更新后应保持一致。
- 隔离决定了一个事务所做的更改如何/何时对另一个事务可见。从严格性的角度来看,Serializable 和 Snapshot Isolation 是排名前 2 位的隔离级别。
- 持久性确保交易结果永久存储在系统中。即使在断电或系统故障的情况下,修改也必须持续存在。
在分布式数据库的上下文中,ACID 事务在内部可以分为以下三种类型
单排 ACID
所有操作仅影响一行(也称为键)的事务称为单行 ACID 事务。由于在大多数分布式数据库中,单行数据通常不会跨越单个节点的边界,因此在分布式数据库世界中更容易实现单行 ACID 事务。然而,大多数 NoSQL 数据库甚至无法支持这种类型的事务,因为它们的最终一致性存储引擎无法对读取的数据的正确性提供内在的保证。值得注意的例外是MongoDB,它支持多文档分布式事务。
单片 ACID
与单行 ACID 相比,单分片 ACID 的边际改进是单分片 ACID,其中事务操作中涉及的所有行都位于分布式数据库的单个分片中。由于单个分片始终位于单个节点内,因此这种风格也不涉及跨多个节点协调事务操作,因此在分布式数据库中更容易实现。例如,MongoDB 宣布打算从其 4.0 版本开始支持单分片事务。
分布式 ACID
在 AiSQL、Google Cloud Spanner 和 Azure Cosmos DB 等自动分片分布式数据库中,分片在创建群集时设计分布在多个节点上。影响分布在多个节点上分片上的一组行的事务称为分布式 ACID 事务。在横向扩展数据库中实现分布式 ACID 事务需要使用事务管理器,该事务管理器可以协调各种操作,然后根据需要提交/回滚事务。流行的 NoSQL 数据库旨在避免这种额外的复杂性,因为担心它们将不得不在性能上妥协(以写入延迟增加和线性可伸缩性降低的形式)。正如我们在下面看到的,这种担忧并非基于现实,确实可以通过具有分布式 ACID 事务和高性能的单个分布式数据库来提高应用程序开发人员的敏捷性。由 Apple 开源的 FoundationDB 遵循类似的设计理念,即使用事务授权处理事务。
AiSQL 中的分布式 ACID 事务
AiSQL 的分片、复制和事务设计灵感来自于 2012 年发表的原始 Google Spanner 论文。目标是以高性能为分布式事务提供服务,并且不影响正确性。由于分布式事务不是应用程序需要的唯一事务类型,因此 AiSQL可以有效地检测和管理涉及单行/单分片事务(使用每个分片共识,没有任何 2 阶段提交)和分布式事务(使用 2 阶段提交)的不同场景。为了简单起见,我们将研究分布式事务如何在分布在多个节点上的多个密钥上运行的一般情况。
步骤 1。选择事务管理器
AiSQL有一个内置的事务管理器来管理事务的生命周期。事务管理器在群集中的每个节点上运行(作为Tserver的一部分),并在添加节点时自动横向扩展。
由于事务管理器是无状态的,因此传入的事务可以路由到任何节点。群集中的任何事务管理器都可以管理传入的事务。为了优化事务的性能,AiSQL查询层 (BQL) 尝试在拥有事务访问的大部分数据的Tserver上调度事务。
第2步。在交易状态表中创建条目
交易需要以可靠和容错的方式进行跟踪。这是必不可少的,因为事务可能会尝试更新多个Keys,但可能会在任何中间步骤中失败或中止。
为了跟踪交易记录,将在交易记录状态表中为交易创建一个新条目。以下信息存储为此事务条目的一部分。
- 事务 ID,这是唯一标识事务的 UUID。
- 状态,可以是挂起、已提交或中止之一。所有事务都以挂起状态开始,然后进入已提交或中止状态,在清理之前,它们将永久保持这种状态。
- 提交混合时间戳,这是事务的提交时间戳。这是用于多版本并发控制 (MVCC) 的最终时间戳,用于在事务提交时提供事务所做的各种更新。
- 参与Tserver的 ID 列表,这些tserver是交易写入的最后一组tserver。在步骤 #6 中阅读有关此内容的更多信息,该步骤描述了如何清理临时写入。
第 3 步。写入临时记录
即使在事务生命周期的这一点上,也无法预测任何更新是否会与另一个事务的更新发生冲突。因此,AiSQL将临时记录写入所有负责事务尝试修改的keys的TSERVER。这些记录是临时的,因为在事务提交之前,读者看不到它们。
除了存储交易更新的数据外,临时记录还充当永久的可撤销锁。这些锁(由临时记录表示)可以通过另一个冲突事务撤销。冲突解决子系统确保对于任何两个冲突事务,至少有一个事务被中止。
此外,还存储了交易元数据记录,以便有效地查找给定交易的以下信息:
- 交易协调员
- 优先级,用于决定中止哪些冲突事务
- 作为交易的一部分写入此tserver的所有临时记录
这样做是为了提高下面步骤 4b 中讨论的事务的提交步骤的性能。
步骤 4a。处理冲突
当多个事务同时运行时,它们可能会尝试更新同一组KEY。发生这种情况时,除非检测到并处理冲突,否则这些更新可能会违反正在运行的事务的隔离保证。冲突更新集取决于操作(读取与写入)和隔离级别保证(可序列化与快照隔离)。
下表列出了必须检测和解决的冲突的简单视图。解决方法通常是在以后可以解决冲突的某个时间点在内部重新启动其中一个冲突事务。
可序列化隔离级别和快照隔离级别的冲突操作
AISQL 目前自动检测并处理快照隔离级别的写写冲突。其他冲突操作将作为即将推出的 Serializable 隔离级别支持的一部分自动处理和解决。
一旦事务管理器成功写入了所有临时记录,它就会通过向事务状态TESERVER发送 RPC 请求来提交事务。仅当事务尚未因冲突而中止时,提交操作才会成功。commit 操作本身的原子性和持久性由事务状态 tablet 的 Raft 组来保证。提交操作完成后,所有临时记录将立即对客户端可见。
提交时间戳被选为将事务提交条目附加到其 Raft 日志时事务状态 tablet 的当前混合时间。然后,它被用作常规记录的最终 MVCC 时间戳,在应用和清理临时记录时替换事务的临时记录。
第 5 步。将确认发送回客户端
提交事务后,事务管理器会向客户端确认事务已成功。
第 6 步。清理临时记录
事务状态TESERVER向参与事务的每个TSERVER发送清理请求。这可以有效地完成,因为参与TSERVER的 ID 列表存储为交易条目的一部分。
清理请求包含具有事务 ID 和提交时间戳的特殊应用记录。收到此请求后,TERSER会删除属于该事务的临时记录,并写入具有正确提交时间戳的永久记录。
一旦所有参与的TSERVER都成功处理了这些申请记录,状态TSERVER就可以删除交易状态记录。状态记录的删除是通过在状态TSERVER的 Raft 日志中写入一个特殊的 applied everywhere 条目来实现的。属于此事务的 Raft 日志条目将在此时间点之后不久从状态TSERVER的 Raft 日志中清除,作为旧 Raft 日志的常规垃圾回收的一部分。
现在,数据已准备就绪,可以按照事务读取路径中所述提供。
在分布式事务中实现高性能
AISQL中使用了许多技术来实现高性能,同时保留分布式 ACID 事务保证。下面概述了其中的一些。
1. 积极缓存正在进行的事务
正在进行的给定事务可能需要查找自己的元数据(例如它已更新的TSERVER)。其他事务可能还希望查找有关冲突事务的信息,并根据相对优先级中止它们。AISQL积极缓存正在进行的事务的信息,以使查找非常快速和高效。
2.细粒度锁定
AISQL支持细粒度锁定,以便执行冲突解决。这对于在面向文档的数据库(AISQL的核心)中处理具有性能的分布式事务至关重要。如果没有细粒度锁,更新文档中非重叠属性的事务最终可能会相互争用。
下图显示了 AISQL 实现的细粒度锁的类型。
例如,如果事务修改了表中一行中的单个列,则可能会采用以下细粒度锁:
- 对行(文档根目录)进行弱锁,以确保该行不能同时被另一个事务删除,但允许更新列的事务并行继续。
- 对要更新的列进行强锁定,以序列化该列的更新,以保证一致性。
3. 安全时间
AISQL中的每个读取请求都会被分配一个特定的混合时间(对于 MVCC)——读取混合时间戳。这允许对同一组KEY的写入操作与读取并行进行,从而确保高性能。
但是,至关重要的是,截至此读取混合时间戳时,数据库的视图不会通过同时发生的写入来更新。这样做是为了确保在重试读取操作时,在特定时间戳连续读取数据不会看到不同的结果。为了实现这一点,AISQL使用了混合时间领导者租赁的概念。AISQL处理以下操作之间错综复杂的依赖关系,以确保正确性:
- 键的单行更新
- 在交易完成之前为一行写入的临时记录
- 从数据库中读取行的值
4. 自动检测和优化单排 ACID 与分布式 ACID
AISQL 经过专门设计,能够在不影响正确性的情况下检测两个流。
- 单行 ACID 事务经过优化,在没有冲突操作时具有较低的延迟。
- 如果存在冲突操作,或者查询正在尝试需要写入临时记录的分布式/多分片事务操作,AISQL 会自动切换到保持正确性,即使这意味着更高的延迟。
因此,AISQL允许使用单行 ACID 语义批处理写入操作,以实现非常高的流摄取性能,同时它允许对一组具有一致性的行进行事务更新。
总结
在开发面向用户的 Web 和移动应用程序时,事务是一项基本功能。通常,此类应用程序中的很大一部分工作负载需要具有单行 ACID 保证的高性能,而一小部分工作负载需要具有极高数据完整性的分布式 ACID 事务。AISQL旨在在解决这两种情况时取得良好的平衡。最终目标是通过一个统一的数据平台从根本上简化应用程序开发,该平台将 SQL、互联网规模增长和全球分发结合在一起。我们继续致力于的两个领域是 Jepsen 测试,用于在故障条件下进行数据正确性验证,以及针对具有分布式事务的工作负载的其他性能基准。请继续关注结果。




