1.CockroachDB: 弹性、地理分布式SQL 数据库
CockroachDB: 弹性、地理分布式SQL 数据库
现代 OLTP 负载正迅速地跨越地域分布,这使得跨国公司必须构建可扩展的应用系统并根据法律法规细粒度地控制数据存放位置。在这种背景下,CockroachDB(CRDB)应运而生,流程审批系统 源码它是一个可扩展的 SQL 数据库管理系统,旨在支持全球性的 OLTP 负载的同时,保持高可用性和强一致性。 CRDB 从头构建,支持在普通商用硬件上实现跨地域的分布式事务,并且能够像蟑螂一样抵御灾难。其创新的事务模型、容错机制和高性能特性使其成为跨国公司理想的选择。此外,CRDB 还提供了 SQL 接口和自动根据数据库集群规模进行伸缩的能力,以满足数据存储和管理的需求。 为了满足跨国公司的需求,CRDB 重点关注以下几个特性:合规性、全屏种植僵尸源码容错性和高性能。它具有前沿的查询优化器和分布式 SQL 执行引擎,支持在线模式更改、备份和恢复、快速导入、JSON 支持以及与外部分析系统的集成等功能。此外,CRDB 的源码已入驻 GitHub,且从 BSL 许可转为 Apache 开源 2.0 协议,用户无需依赖第三方 SQL 扩展专利或受制于云供应商宕机风险,避免了供应商锁定问题。 本文将详细介绍 CRDB 的各个组成部分,包括架构、复制和数据分布机制、事务模型、时间戳排序、SQL 数据模型、执行和模式变化、对于负数的源码性能评估和案例学习、经验总结、相关著作以及结论与展望。接下来,我们将从系统架构角度深入剖析 CRDB 的设计与实现。系统架构概述
CRDB 使用无共享架构(share-nothing),所有的节点都同时提供存储和计算能力,集群可以包含任意数量的节点,这些节点可以在同一数据中心或分布于全球。客户端可以连接集群中的任何一个节点。 CRDB 的架构可以分为以下几层:SQL 层
最顶层是 SQL 层,它是所有用户与数据库交互的接口。它包括解析器、优化器和 SQL 执行引擎,该引擎将高级 SQL 语句转换为底层 key-value (KV) 存储的低级读写请求。 通常,SQL 层并不了解数据是如何分区或分布的,因为下面的网站app源码开源层抽象了一个单体的 KV 存储。然而,在第 5 节中,我们将详细介绍某些查询如何打破这种抽象,以实现更高效的分布式 SQL 计算。事务 KV 层
来自 SQL 层的请求被传递到事务 KV 层,该层确保跨越多个 KV 对的原子性更改。它在很大程度上对 CRDB 的隔离保障负有责任。这些原子性和隔离保证将在第 3 节和第 4 节中详细描述。数据分布层
这一层抽象了按 key 排序的单体逻辑键空间。在这个键空间中,所有数据都是可寻址的,无论是系统数据(用于内部数据结构和元数据)还是用户数据(SQL 表和索引)。 CRDB 对 key 进行范围分区,将数据分成连续有序的,大小约为 MB 的块,我们把这些块叫做“Ranges”。这些 Ranges 之间的排序由一个两层索引结构维护,保存在一系列系统级别 Rranges 里面,源码直接变补码并被预缓存以支持快速的按 key 查询。本层负责确定查询的某个子集应该由哪个 Range 处理,并合理路由。 MB 的 Range 足够小,可以允许快速迁移,又足够大,足以保存一块连续的经常一起被访问的数据。Ranges 的初始状态为空,随着尺寸变化,经历分割、合并。Ranges 分割还可以根据负载进行,以减少 CPU 热点与资源不平衡。数据复制层
默认情况下,每个 Range 保存 3 个副本,每个副本存储在不同的节点上。在第 2.2 节中,我们将描述复制层如何使用基于共识的复制确保修改的持久性。存储层
这是最底层,代表一个本地磁盘支持的 KV 存储。它提供了高效的写和范围扫描,以支持高性能的 SQL 执行。在撰写本文时,我们依赖的是 RocksDB,它在其他地方有详细的记录,本论文中将其作为黑盒处理。容错和高可用性
使用RAFT复制
一个 Range 的所有副本组成一个 Raft group,其中一个副本是持久的 leader,协调所有发给这个 Raft group 的写操作,其他副本是 follower。复制的单元是命令,代表要存储层处理的一个编辑序列。Raft 在每个 Range 的所有副本范围内,维护一个一致的、排序的更新日志,每个副本各自按顺序在其本地存储引擎里应用那些已经声明被提交的日志。 CRDB 使用 Range 层面上的租约,其中一个副本(通常是 Raft group leader)承担 leaseholder 角色,因此是唯一允许提供权威最新读取或提交写请求给 Raft group leader 的副本。所有写操作都经过了 leaseholder,因此所有的读都可以在不牺牲一致性的情况下绕过 Raft 所需的网络往返成本。 用户级 Ranges 的租约和 leaseholder 所在节点的存活性绑定,存活性通知通过节点每 4.5 秒发送一个特殊心跳到系统级 Range 实现。系统级 Range 转而使用基于到期的租约,必须每 9 秒更新一次。如果某个节点探测到 leaseholder 不存活了,它就尝试自己获取租约。 为了确保每个时间点只有一个副本拥有租约,租约获取在现有的 Raft 框架内完成,提交一个特殊的获取租约日志记录。每个租约获取请求包含一个它在请求时认为合法的租约数据,两个副本的请求内的租约不重叠就可以达成这个保证。在第 4 节中,我们还会讨论租约不重叠是 CRDB 隔离机制的前提。成员变化与自动负载(再)平衡
集群运行中,节点可能加入或离开该集群,也可能暂时或永久失败。CRDB 使用相同的方法应对这些场景:在最新的存活节点中间重新分配负载。 节点短暂失败,而多数节点仍然可用的情况下,CRDB 可以持续运行。如果失败的是 Raft group 的 leader,Raft 保证新 leader 的选举。失败节点上先后可以重新加入原来的 group,同伴们帮它追赶错失的更新操作。方法包括:1)发送全量 Range 数据快照给它 2)发送错失的 Raft log 记录集合给它。具体选择根据该副本节点不可用期间错失的操作量作出。 节点长时间失败,CRDB 自动根据存活的副本为复制等级不够的 Ranges 创建出新的足够的副本。其存放位置由下一节描述选择。决策依赖的相关数据比如,存活节点信息、集群监测指标使用点对点的 Gossip 协议分发。副本存放
支持手动和自动选择。 手动选择需要用户为每个节点配置属性,比如节点特性(特殊硬件、RAM、硬盘类型...)、节点位置(国家、地区、可用 zone...)。还可以在表模式里指定限制、偏好,比如指定 region 列,可以用来帮助分区,和把分区映射到特定地理区域。 自动选择根据用户制定的规则和不同的启发式算法自动跨失败域分布副本,容错不同程度的失败(硬盘级、机架级、数据中心级、区域级别)。数据存放策略
CRDB 的副本存放和 leaseholder 存放机制支持广泛的数据存放策略,用户可以借此做到数据合规,并在性能和容错间合理取舍。以下是一些多区域模式。 本文篇幅较长,将分为三篇发布。