YugabyteDB 架构– 查询层
Karthik Ranganathan【hudson译】
2019 年3月18
我们前一篇博文深入探讨了称之为DocDB的 YugabyteDB 存储层 ,一个受 Google Spanner 启发的分布式文档存储. 这篇博文将聚焦Yugabyte DB 查询层:YSQL, 一个由DocDB强力支撑的分布式的,高度弹性,PostgreSQL-兼容的 SQL API层.随后一篇博文将总结工程化这样的数据库时所面临的一些挑战和吸取的教训 .
YSQL使分布式PostgreSQL变为现实
Yugabyte SQL (YSQL) 是一个分布式的并且高度弹性的SQL 层, 跨越多个节点运行. 它与PostgreSQL的SQL方言和连接协议兼容. 这意味 着熟悉 PostgreSQL的开发者可以完全复用他们的知识 (和标准的 PostgreSQL 客户端驱动) 来创建由 YSQL支持的应用程序.YSQL实质上将单体PostgreSQL数据库转换为DocDB支持的分布式数据库,因此为此,它尽可能重用开源PostgreSQL的查询层(用C编写)。
.
下面是我们为早期的 YSQL设定的设计目标.
- 复用开源的, 成熟和特性丰富的 PostgreSQL 查询层
- 保留现成的 PostgreSQL 功能 ,并在需要时进行扩充
- 通过以模块化方式实现功能特性以支持迁移到较新版本的PostgreSQL
为实现上述目标而不懈的努力已经带来了丰厚的回报. YSQL现在支持比我们最初预期的更广泛的现有PostgreSQL功能。从v1.2特征矩阵中可以看出这一点,例如:
- DDL 语句:
CREATE, DROP和 TRUNCATE - 数据类型: 所有原始类型 包括数值类型 (整数和浮点数), 文本数据类型, 字节数组, d日期时间类型,
UUID, SERIAL, 以及JSONB - DML 语句: 大多数诸如
INSERT, UPDATE, SELECT和DELETE语句. 现在支持核心SQL的批处理功能, 包括连接, WHERE子句,GROUP BY, ORDER BY, LIMIT, OFFSET和SEQUENCES - 事务:
ABORT, ROLLBACK, BEGIN, END,和COMMIT` - 插件: 大量丰富的 PostgreSQL 内建的函数和操作符
- 其他特性:
VIEWs, EXPLAIN, PREPARE-BIND-EXECUTE, 和 JDBC 支持
至于迁移到较新版本的设计目标,YSQL最初基于PostgreSQL v10.4,最近将重新以PostgreSQL v11.2为基础 。上述工作将在在几周内完成 。
YSQL 是如何工作的?
YSQL 内部可以分为四个独立的区域 :
- 系统目录管理
- 用户表管理
- 读和写的IO路径
- 将SQL 表映射到文档存储
下一节将详细介绍上述各个方面。在深入讨论细节之前,这里简要回顾一下本系列文章中第一篇DocDB.
- DocDB 中的每一个表都有相同的模式: 一个键映射到一个文档。
- 既然是分布式数据库,每次写数据都要复制(到其他节点)。
- 提供单键的线性可伸缩性以及多键快照隔离级别 (串行化隔离级别也在开发中)。
- 本地支持文档任意属性的二级索引。
- 高效查询和更新文档任意属性子集。
系统目录管理
PostgreSQL 文档关于系统目录是这样描述的: 系统目录是一些普通表,存放模式的元数据 , 比如关于表的信息,表的字段信息 , 以及内部簿记信息。 PostgreSQL 的 initdb 代码路径完全不同于处理普通用户表的代码路径 , 它创建并初始化系统目录表 。 因此, 为避免分布式SQL数据库的单点故障 , 复制系统目录非常必要。
1. 通过 initdb初始化系统目录
当 YSQL 第一次启动时 , 一个修改的 initdb 开始执行,并在DocDB中创建系统目录 ,一个可以复制的,单个tablet的系统目录表 。 如上图所示 。
DocDB中系统目录的 tablet 形成了 一个Raft组, 可以在一组节点中复制数据,且容忍一些失败 。在上图中 , 实线边框表示系统目录 tablet的领导者, 虚线边框是跟随者, 确保PostgreSQL仍然可以依赖熟悉的系统目录来运行。
2. 准备为应用提供服务
一旦创建了系统目录,应用程序就可以使用YSQL。由于数据跨节点复制并持久化到磁盘上,因此在集群的后续重新启动不需要initdb。
用户表管理
现在 YSQL 集群已经启动并运行, 让我们考虑当用户创建表时这样一个场景,分为四个步骤。
1. 解析和分析查询
和 PostgreSQL 数据库完全一样, 查询被 PostgreSQL 服务进程接收 – 然后解析,分析和执行。
2. 将查询路由到 DocDB 系统目录tablet的领导者
对于常规PostgreSQL,执行阶段将向系统目录表添加条目,并在本地文件系统上创建一些目录和文件。对于YSQL,系统目录的更新将发送到DocDB中分布式系统目录表tablet的领导者。
3. 在DocDB的所有节点间复制系统目录条目
DocDB中分布式系统目录表的Tablet领导者负责将更新复制到跟随者。这通过使用Raft共识来完成,确保即使存在故障的情况下,更新也是线性化的。
4. 在DocDB中创建用户表
既然条目已经保存在系统目录中,执行阶段的下一步是创建分布式DocDB表。这涉及跨一组节点创建tablets(具有副本)。如下图所示。
完成上述步骤后,就可以使用该表了。
读/写 IO 路径
读和写IO路径非常相似。让我们了解写IO路径,它涉及DocDB中数据的复制。读取IO路径类似,除了最后一步,它可以直接从DocDB中的tablet领导者读取数据。
1. 解析并分析查询
与PostgreSQL一样,PostgreSQL 服务器进程接收查询。然后它经过解析器、分析器、规划器和执行器。然而,为了适应分布式数据库而不是本地存储,一些规划、分析和执行步骤是不同的。
2. 将插入路由到 tablet 的领导者
SQL insert语句可能会更新一行或多行。尽管DocDB可以在本机处理这两种情况,为提高YSQL的性能,这两种情形的检测和处理方式不同。单行插入将直接路由到拥有该行主键的tablet领导者。每个节点都有一个内置的事务管理器来协调分布式事务。影响多行的插入由该事务管理器处理(在首先接收请求的节点处)。单行插入情况如下所示。
3. 通过Raft协议复制写操作
在单行插入的情况下,tablet领导者使用Raft协议将数据复制到跟随者节点。下面显示了更简单的情况。在多行插入的情况下,事务管理器跨越tablet(通常在不同节点上)写入多个记录(事务状态记录、临时记录等)。每个写入都使用Raft共识进行复制。集群中的混合逻辑时钟或HLC用作粗粒度的同步、高可用的全局时钟,以协调写入。其结果是:写入操作是一个具有容错性的高性能系统。
I
将 SQL 表映射到文档
YSQL中的每个用户表映射到具有多个tablet相应DocDB表。YSQL表有自己的模式,而所有DocDB表都有相同的模式,如下所示。实际的模式实施是使用表模式元数据完成的。
DocKey → { Document Value }
主键列值的组合集合用于构造上面的文档键。每个值列(非主键列)都映射到上面文档值中的一个属性。各种不同的YSQL结构被映射到合适的DocDB等价对象。如下表所示。
那么这在实践中看起来如何?让我们举个例子。考虑下面这个相当简单的表。
CREATE TABLE msgs (
user_id TEXT,
msg_id INT,
subject TEXT
msg TEXT,
PRIMARY KEY (user_id, msg_id);
这将对应于具有文档键到值模式的DocDB表。现在,让我们在时间T1执行以下插入。
T1: INSERT INTO msgs (user_id, msg_id, subject, msg)
VALUES ('user1', 10, 'hello', 'hello world');
这将转换为DocDB表中的以下条目。
DocKey ('user1', 10):
{
column_id (subject), T1 -> 'hello',
column_id (msg), T1 -> 'hello world'
}
YSQL 优势
对于应用程序,YSQL集群显示为单个逻辑PostgreSQL数据库。YSQL层中的所有节点都是相同的,应用程序客户端可以连接到任何节点以读取或写入数据。除了最大限度的PostgreSQL兼容性之外,这种架构还提供了许多好处。
水平写扩充性
由于DocDB能够按需扩展,因此无状态YSQL层可以方便地按需添加节点。当需要更多资源(CPU、内存、存储容量)时,快速扩充集群即可。
高度弹性 w/ 本地故障转移 & 修复
底层DocDB集群是容错的。这意味着节点故障不会影响使用该分布式SQL数据库的SQL应用程序。它只是简单地开始与一个新节点通信,这和原生PostgreSQL相反,后者常见方法是主从复制,不可避免地导致手动故障切换和/或无法为最近的提交提供服务。
地理分布 w/ 多区域部署
DocDB 支持地理分布的部署, 这意味着你可以跨越不同的地理区域或者时区部署一个分布式的数据库。
云原生操作
DocDB 允许动态改变节点而不影响应用。 模式变更以及基础设施移植现在是零停机 , 即使是对一个 SQL数据库也是如此。
总结
将Spanner 和 PostgreSQL这两个标志性的数据库技术结合在一起形成一个新的开源的,云原生的数据库是一个及其令人满意的工程成就 ,然而我们理解一个精心设计的数据库就其本身而言并不能够在开发者和架构师的心目中建立信任。 我们必须通过传统的沟通、合作和分享成功案例等方式赢得信任。
通过本系列博文, 我们解释了我们的设计原则和与这些原则相关的一些权衡, 实际的实现细节,最后,特别是围绕一些更具挑战性的方面所吸取的经验教训。我们打算通过详尽的正确性测试(如Jepsen)和全面的性能基准测试来证明我们的主张 (包括TPCC)。今年夏天,随着我们在YSQL GA方面取得快速进展,我们正在与一些当前用户密切合作,以揭示YSQL如何补充他们在YugabyteDB的现有投资。如果您的项目也能从YSQL中受益,不要犹豫,请通过我们社区的Slack 频道随时联系我们。