KaiwuDB| Google Spanner 经典架构回顾

news2025/4/21 1:04:16

前言

大数据时代 ,随着移动互联网和物联网技术的发展 , 全球数据量呈现爆发式增长,已经远远超出集中式单机数据库的处理能力。CCF 数据库专委 2021 年发布的《“十四五”数据库发展趋势与挑战》显示, 各行各业海量数据的管理需求给数据库产业的发展带来了诸多挑战 :

• 海量数据存储
传统集中式数据库容量大多在百 GB 至 TB 级别,伴随着各行各业数据爆发式增长,数据库所需存储的数据容量也在急剧增长且数量呈现指数级增长,从 TB 级别增加到 PB 级别,未来很快就会增加至 EB 级别;

• 海量并发访问
数据库的业务访问量也发生了量到质的变化,传统的业务模式业务访问仅局限于企业内部 , 通常数百至数千并发即可满足。而在互联网及物联网模式下,数据库的访问服务于海量的联网终端用户,需要万级至百万级的并发支持能力;

• 基础设施的分布式
为了提升数据库的高可用,各行业在加速信息化基础设施的建设,从传统的两地三中心向分布式多地多中心变化,要求数据库从传统的可用区(AZ)内部署向跨 AZ、跨地域(region)的分布式部署架构演进。

基于上述背景,传统集中式数据库架构已无法有效满足新型互联网业务模式下的业务需求,而解决这些问题本质是要解决数据库扩展性问题,分布式数据库架构应运而生。根据时间线,现有解决方案大致可分为 3 个阶段 :

• Step 1:将现有成熟关系型数据库(如 Oracle、SQL Server、MySQL 和 PostgreSQL 等)在分布式集群或者云平台上进行小规模扩展和部署;

• Step 2:放弃关系模型和已有的 ACID 事务特性,转向选择具备灵活的 schema-free 数据模型、高可用性和最终一致性特性的 NoSQL 数据库;

• Step 3: 融合关系型数据库和 NoSQL 二者优势的新型 NewSQL ** 数据库。

在这篇技术博客中,我们将深入探讨 Google 于 2012 年在系统领域国际会议 USENIX Conference on Operating Systems Design and Implementation(简称 OSDI)上发表的论文《Spanner:Google’s Globally Distributed Database》中所提出的经典分布式数据库 Spanner 架构及其核心技术。通过这些内容 , 你将了解到 Spanner 如何在当时突破了技术瓶颈 ,为后续的分布式数据库技术发展奠定了基础。

Spanner 架构

Spanner 是谷歌提出的一种可扩展、支持多版本、全球分布和同步复制的数据库系统。作为全球第一个在大规模数据中心中实现强一致性和同步复制的数据库系统,Spanner 彻底颠覆了传统数据库的设计理念。该论文主要描述了 Spanner 的架构、关键特性、相关设计的基本考量。接下来,我们将对 Spanner 基础架构、TrueTime 机制、事务并发控制等关键特性进行详细介绍。
在这里插入图片描述

上图为 Spanner 整体架构,其中:

• Universe:一个 Spanner 部署被称为一个 universe。一个 Universe 下面包含多个 Zones,Zone 是 Spanner 管理、物理隔离和数据复制的基本单位。Universe 通过 universe master 组件来显示其所管理的所有 zone 的运行状态,通过 placement driver 来处理跨 Zone 的数据移动以保持数据均衡。

• Zone:一个数据中心可以对应一个或多个 Zone,Zone 也可以随着数据中心的搭建和拆除而动态添加和删除。每个 Zone 包含一个 zone master、一个 location proxy 和多个 span server。其中,zone master 负责将数据分配到 Spanner server,location proxy 作为代理中间件负责帮助客户端定位数据所在的 span server,而诸多的 span server 则用于存储数据。

Spanner Server 软件栈

通过上述架构组件作用描述可以看出,span server 是 Spanner 的核心组件,其软件栈实现如图所示。
在这里插入图片描述

每个 span server 负责管理 100 到 1000 个 tablet。其中 , tablet 是存储数据的基本单元,类似于 BigTable 中 tablet 的组织方式 , 实现了以下键值映射:(key:string, timestamp:int64) -> string。然而不同于 BigTable,Spanner 为每个数据额外分配了一个时间戳 , 使其成为一个多版本数据库而不是简单的键值存储。tablet 的状态存储在一组类似 B-tree 的文件和一个预写日志中,这些文件和日志都存储在 Google 的分布式文件系统 Colossus 上。

为了支持数据复制且保证数据多副本的一致性, span server 在每个 tablet 上运行有一个 paxos 状态机。每个状态机将其元数据和日志都存储在对应的 tablet 上。为了提升 Spanner 系统性能和可用性,其对 paxos 共识机制进行了多项优化 :

• 通过对 paxos 进行优化,Spanner 支持为期 10s 的长租约 leader 机制 , 从而减少了频繁选举的开销。

• 为了确保数据的一致性和可恢复性 , Spanner 将每个 paxos 写操作记录两次,一次记录在 tablet 的日志,一次记录在 paxos 本身的日志中。

• 实现流水线式 paxos,进一步提高了系统在高并发情况下的吞吐量。

在 Leader 副本上,每个 Spanner server 都会维护一个锁表和一个事务管理器,其中 :

• 锁表用于实现并发控制,包含了所有数据记录对应的两阶段锁状态。

• 事务管理器用于支持分布式事务。如果一个事务只涉及一个 paxos 组, 这种情况下事务就会绕过管理器,直接基于锁表和 paxos 共识协议就可以保证事务一致性。如果一个事务涉及多个 paxos 组,这些组中的 leader 会协调执行两阶段提交。具体的,这些组中的一个组会被选定为协调者,该组中的 leader 作为协调领导者角色,该组中的 slave 副本则作为协调追随者角色。基于两阶段提交协议,在准备阶段,协调者向所有参与者发送准备请求;在提交阶段,协调者向所有参与者发送提交请求。值得一提的是,每个事务管理器状态也都存储在底层的 paxos 组中以保证状态一致性。

目录 directories

在前文中曾提到,Spanner 支持跨 Zone 的数据移动以保持数据均衡。为实现对该功能的支持, Spanner 在底层 key-value 数据映射基础上,封装实现了一层桶抽象,并将其命名为 directory。作为数据放置的最小单元,它包含共享公共前缀的一个连续 Key 记录集合。

同一个 directory 的数据拥有相同的副本配置项, 因此当数据在 paxos 组中移动时,directory 也是最小的移动单元 , 其过程示意如图所示。
在这里插入图片描述

这种基于 directory 的数据移动具有如下好处:

• directory 通过控制数据在不同数据中心的分布,可以优化数据访问的延迟。通过将 directory 分配到靠近用户的位置,可以显著降低读取延迟,提高用户访问速度;

• 可以将总是同时访问的 directory 放入同一个 paxos 组内,减轻负载。

实际上,我们可以将 directory 理解为 partition 的一种逻辑上的表达。

数据模型

Spanner 支持应用程序在 universe 中创建一个或多个数据库,每个数据库可以包含无限量的模式化表。一个 Spanner 的建表语句实例如图所示,可以看出 Spanner 与关系型数据库类似 , 每张表包含行、列和版本值。每张表要求必须有一个或多个主键(primary key)列 , 定义了主键列到非主键列的映射 , 这使得 Spanner 看起来也非常像是一个 key-value 存储。
在这里插入图片描述

但是与关系数据库不同 :
1)Spanner 表中的行都有对应的名称 , 该名称是由该行对应的主键构建形成的。这种数据模型使得应用程序能够通过 key 的选择来控制获知数据的位置。

2)Spanner 表间不存在外键关系 , 而是设计了一种层级关系 hierarchy。以图 4 示例为例 , Users 表被称为 directory 表或父表 , 用 DIRECTORY 关键字表示 , 子表 Albums 与 directory 表交织(interleave),交织的表数据被保存在一起。

TrueTime 机制

通过前面的介绍,我们可以看出,Spanner 的设计离不开时间戳,但如何能够提供精确且一致的时间戳,是 Spanner 的关键问题之一。分布式环境下各个机器时钟的误差导致我们无法通过简单地调用系统时间函数来生成唯一的时间戳,从而无法保证事务的外部一致性,而中心化的全局授时服务方案又面临系统的高可用问题。

为了解决上述问题,Spanner 选择采用了一种纯粹的分布式方案,称之为 TrueTime 机制。该机制保证了 Spanner 在序列化隔离级别的基础上,能够提供更严格的一致性,即外部一致性:即如果事务 T1 在事务 T2 之前提交,那么事务 T1 的时间戳一定小于事务 T2 的时间戳。

为了实现上述外部一致性,TrueTime 实现的关键特性之一是显式地暴露时钟的不确定性。它不仅提供当前时间,还提供一个时间范围,即最早可能的时间和最晚可能的时间。TrueTime 一共包含以下三种方法:
在这里插入图片描述

在 TrueTime 中,时间被定义为 TTinterval,代表一个时间区间。TT.now() 方法用于获取当前时间,返回一个时间范围 [earliest, latest],该区间包含了当前的真实时间。TT.after(t) 和 TT.before(t) 是 TT.now 的包装函数,判断时间 t 是否已经绝对过去或者绝对没有到来:

• TT.after(t) == true 表示 now().latest < t
• TT.before(t) == true 表示 t < now().earliest

在实际应用中,调用 TT.now 得到时间范围后,可以使用这个范围的任意时间作为当前时间。显然,无论如何选择这个时间,都有可能与当前时间不一致。即使所有事务都采用 latest 作为时间戳,也不能保证时间戳的先后顺序与调用 TT.now 时的时间顺序符合。因此,为了达成外部一致性,这里存在一个必须要处理的误差。Spanner 采用一种叫做 commit wait 的方式来处理这种误差。
当事务启动后,Spanner 会为该事务选择一个时间戳,要求它一定大于事务实际开始的时间,小于事务实际结束的时间,即

在这里插入图片描述

为了满足该条件,设计以下两个原则:

  • 事务时间戳一定要大于 TT.now().latest
  • 直到 TT.after(s) == true,才提交事务

基于上述原则,commit wait 的判定流程如下。假设一个事务在 t_start 开始启动,事务启动后,调用 now() 方法,因此 t_start 一定小于 now() 的实际调用时间,并且 now() 的实际调用时间一定小于 now().latest。Spanner 选择一个时间戳 s,该时间戳大于 now().latest,因此:
在这里插入图片描述

在选择 s 后,调用 TT.after(s),返回 true 后提交事务。TT.after(s) == true 因为 s < now().latest(备注:TT.after(s) 是 now() 的包装函数),而 now() 的实际调用时间一定大于 now.eariest,小于 t_end,从而得到:
在这里插入图片描述

进一步又得出:

在这里插入图片描述

通过上述流程,我们选择一个未来的时间戳,该时间一定大于事务的开始时间,并通过等待足够长的时间,使这个未来的时间戳成为过去时间,从而小于事务的提交时间。

在实际实现中,Spanner 综合利用了硬件原子钟和 GPS,保证了不同机器之间的时间差不超过 ε,进而从某种程度上把各个节点的时钟拉到了同一个时钟参考系,让不同节点发生的时间可以进行先后的比较。进一步结合上述原理落地实现了其 TrueTime 机制。

事务处理

Spanner 支持事务处理,按照读写操作可以将其分为读 / 写事务,只读事务和快照读事务三类。在一个事务中可以包含对多行数据的多个操作。读 / 写事务包含读操作和写操作。前面我们提到,Spanner 实现了多版本控制。在只读事务中只包含读操作,自动选择最新时间的版本读取。快照读事务允许用户指定一个时间读取数据。
Spanner 支持序列化隔离级别,因此不会丢失数据的正确性。 在此基础上,为了获得更好的性能,结合多版本机制,只读事务和快照读事务可以不影响读 / 写事务的性能,使得 Spanner 成为了时间维度多版本的数据库,大大提升了性能表现。
接下来,我们将介绍 Spanner 读写事务和只读事务的具体实现。

读写事务

Spanner 的读写事务基于 paxos 和两阶段提交(2PC)协议。Spanner 会在参与事务的 paxos 组中选举出一个组作为协调者,进行协调、选取时间戳并进行 2PC 的操作。其中,选举出的组叫做协调者组,组中的 leader 叫做协调者 leader,其他节点叫做协调者从节点。其他的组叫做参与者组,组中的 leader 叫做参与者 leader,其他节点叫做参与者从节点。
具体事务流程如下:

• 读:获取对应的 paxos 组的锁,读取最新版本的数据

• 写:将数据暂存入客户端内存,不先写入数据库,以保证未提交数据对其他客户端 不可见

• 两阶段提交,过程如下:

a)客户端将内存中的数据和 commit ** 请求发送到对应的 paxos 组的 leader
b)参与者 leader 获取上述写入数据对应的写锁,分配一个 prepare 时间戳 t prepare ,对 prepare 消息进行 paxos 写,并发送 prepare 消息到协调者 leader
c)协调者 leader 获取写入数据的写锁,接收到所有 prepare 消息后,分配 commit 时间戳 t commit ,要求大于接收到的最大 prepare 时间戳和 TT.now().latest
d)协调者 leader 进行 commit wait,等到 TT.after( t commit ) 为 true,进行提交操作
e)协调者对 commit 消息进行 paxos 写,并发送 commit 消息给参与者 leader,提交数据,释放锁f)参与者 leader 接收到 commit 消息,进行 paxos 写,调整时间戳,提交数据,释放锁。
只读事务
只读事务无需协调时间戳和 2PC 操作。具体流程如下:
• 客户端向任意 paxos 组的 leader 发起请求,使该 leader 分配一个 start 时间戳 t start ,要求大于 TT.now().latest。
• 读:客户端携带上述时间戳,向相应的 Paxos 组中的 leader 发送读请求。等待该时间戳之前的事务结束后,开始读取数据。
• 事务提交, paxos group 的 leader 进行 commit wait,即等到 TT.after( t start ) 为 true 后,进行提交。
总结
Google Spanner ** 通过引入 TrueTime API 和 paxos 协议,实现了全球范围内的数据一致性和高可用性。其独特的数据模型和分布式事务处理机制,使其成为当时处理大规模、复杂数据的理想选择,并为后续分布式数据库技术的发展奠定了基础。
参考文献
[1] Corbett J C, Dean J, Epstein M, et al. Spanner: Google’s globally distributed database[J]. ACM Transactions on Computer Systems (TOCS), 2013, 31(3): 1-22.
[2] 《分布式系统与一致性》 – 陈东明
[3] blog.csdn.net/qq_40229166…
[4] levy5307.github.io/blog/spanne…
[5] zhuanlan.zhihu.com/p/515895443

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2309710.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

拼电商客户管理系统

内容来自&#xff1a;尚硅谷 难度&#xff1a;easy 目 标 l 模拟实现一个基于文本界面的 《 拼电商客户管理系统 》 l 进一步掌握编程技巧和调试技巧&#xff0c;熟悉面向对象编程 l 主要涉及以下知识点&#xff1a; 类结构的使用&#xff1a;属性、方法及构造器 对象的创建与…

项目准备(flask+pyhon+MachineLearning)- 1

目录 这是一篇学习笔记 1. 搭建项目 2.前期准备工作 3.创建用户(user)模板 这是一篇学习笔记 目的&#xff1a;用flask快速实现发布有关机器学习的项目&#xff0c;掌握flask框架&#xff0c;机器学习模型的存储和调用。 1. 搭建项目 使用pycharm创建项目&#xff0c;fl…

1.2.2 使用Maven方式构建Spring Boot项目

本次实战通过Maven方式构建了一个Spring Boot项目&#xff0c;实现了简单的Web应用。首先&#xff0c;创建了Maven项目并设置好项目名称、位置、构建系统和JDK等。接着&#xff0c;添加了Spring Boot的父项目依赖和web、thymeleaf起步依赖。然后&#xff0c;创建了项目启动类He…

C++中函数的调用

************* C topic&#xff1a;call functions ************* 1、为什么要用函数 In every codes, functions are the crucial parts. There are many advantages of the functions. But I introduce two of them. The first usage of the functions is reuse. And th…

【Linux】之【Bug】VMware 虚拟机开机 一直卡在黑屏左上角下划线闪烁界面

解决 参考&#xff1a; 解决Ubuntu20.04 开机黑屏光标闪烁进不去系统 Centos根目录100%解决思路 当前界面 ctrlaltf3-f6 暂时进入终端界面 df -h 查看发现根目录 磁盘空间已满 执行命令 查看当前目录占用内存明细 sudo du -h -x --max-depth1清理无用的大内存文件 或者安装…

RT-DETR融合YOLOv12中的R-ELAN结构

RT-DETR使用教程&#xff1a; RT-DETR使用教程 RT-DETR改进汇总贴&#xff1a;RT-DETR更新汇总贴 《YOLOv12: Attention-Centric Real-Time Object Detectors》 一、 模块介绍 论文链接&#xff1a;https://arxiv.org/abs/2502.12524 代码链接&#xff1a;https://gitcode.com…

【前端基础】Day 8 H5C3提高

目录 1. HTML5新特性 1.1 新增语义化标签 1.2 新增多媒体标签 1.3 新增input类型 1.4 新增表单属性 2. CSS3的新特性 2.1 新增选择器 2.1.1 属性选择器 2.1.2 结构伪类选择器 2.1.3 伪元素选择器&#xff08;重点&#xff09; 2.2 CSS3盒子模型 2.3 CSS3其他特性&a…

NL2SQL-基于Dify+阿里通义千问大模型,实现自然语音自动生产SQL语句

本文基于Dify阿里通义千问大模型&#xff0c;实现自然语音自动生产SQL语句功能&#xff0c;话不多说直接上效果图 我们可以试着问他几个问题 查询每个部门的员工数量SELECT d.dept_name, COUNT(e.emp_no) AS employee_count FROM employees e JOIN dept_emp de ON e.emp_no d…

Java 9 到 Java 21 新特性全解析:从语法简化到API增强

一、新特性的概述 纵观Java这几年的版本变化&#xff0c;在Java被收入Oracle之后&#xff0c;Java以小步快跑的迭代方式&#xff0c;在功能更新上迈出了更加轻快的步伐。基于时间发布的版本&#xff0c;可以让Java研发团队及时获得开发人员的反馈&#xff0c;因此可以看到最近…

skia的学习与研究

最近再研究skia,特地发一篇文章来记录一下。Skia版本更新非常频繁&#xff0c;大概每四周就会创建一个新版本&#xff0c;此版本持续维护六周左右就会被标记为稳定分支&#xff1b; skia三套渲染&#xff1a; 无gpu硬件如嵌入式设备&#xff0c;使用CPU渲染&#xff0c;使用…

网络编程 day01

网络编程 day01 0. 网络编程课程介绍1. 认识网络1.网络发展史2.局域网与广域网局域网&#xff08;LAN&#xff09;广域网&#xff08;Wan&#xff09; 3.光猫4.路由器5.交换机与路由器6.网线 2. IP1. 基本概念2. 网络号/主机号&#xff08;二级划分&#xff09;3. IP地址分类整…

vscode通过ssh远程连接(linux系统)不能跳转问题

1.问题描述 unbantu中的vscode能够通过函数跳转到函数定义&#xff0c;而windows通过ssh连接unbantu的vscode却无法跳转 2.原因&#xff1a; 主要原因是这里缺少插件&#xff0c;这里是unbantu给主机的服务器&#xff0c;与ubantu本地vscode插件相互独立&#xff0c;能否跳转…

unity pico开发 五 UI交互

文章目录 添加画布添加交互组件取消传送射线对UI的控制解决按扳机键会传送的冲突按下按键呼出菜单&#xff0c;并让菜单出现在头的前方 添加画布 创建一个新画布&#xff0c;添加一个Button&#xff0c;将画布改为world space&#xff0c;然后缩放改为0.001&#xff0c;调整到…

软开经验总结

文章目录 软开经验总结一、二次开发时候操作步骤二、logger的作用&#xff01;&#xff01;&#xff01;三、git使用 软开经验总结 一、二次开发时候操作步骤 改 SDK 和 language level改 maven 配置改数据库 注意Mysql 版本 差别是否过大&#xff01;&#xff01;&#xff0…

QT 中的元对象系统(三):QObject深入理解

目录 1.简介 2.特性 2.1.对象树与内存管理 2.2.信号与槽机制 2.3.事件处理 2.4.属性系统 2.4.1.Q_PROPERTY配置的属性 2.4.2.动态属性 2.4.3.实现原理 2.5.国际化支持 2.6. 定时器支持 3.类设计(q和d指针) 4.总结 1.简介 QObject这个 class 是 QT 对象模型的核心&…

二、QT和驱动模块实现智能家居-----问题汇总1

1、文件地址改变后必须在QT下更改地址 2、指定了QT内Kits下的Sysroot头文件地址&#xff0c;但是还是找不到头文件&#xff1a; 3、提示无法执行QT程序&#xff1a;先干掉之前的QT程序 ps //查看程序PIDkill -9 PID 4、无法执行QT程序 1&#xff09;未设置环境变量 …

Golang的数据库分库分表

# Golang的数据库分库分表 什么是数据库分库分表 数据库分库分表是指将单一的数据库拆分成多个库&#xff0c;每个库中包含多张表&#xff0c;以提高数据库的性能和可伸缩性。通常在大型应用中&#xff0c;单一的数据库往往无法满足高并发和海量数据的需求&#xff0c;因此需要…

NModbus 连接到Modbus服务器(Modbus TCP)

1、在项目中通过NuGet添加NModbus&#xff0c;在界面中添加一个Button。 using NModbus.Device; using NModbus; using System.Net.Sockets; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Docu…

基于vue3和flask开发的前后端管理系统(一):项目启动准备

准备工作 我们需要准备以下工具 vue3&#xff1a;构建前端 tailwind css&#xff1a;样式库vite&#xff1a;快速构建vue项目pinia &#xff1a;vue3 的事件管理器 flask&#xff1a;后端代码Mysql&#xff1a;数据库 heidisql&#xff1a;数据库图形化界面 vscode&#xff1…

单例模式(线程案例)

单例模式可以分为两种&#xff1a;1.饿汉模式 2.懒汉模式 一.饿汉模式 //饿汉模式&#x1f447; class MySingleTon{//因为这是一个静态成员变量&#xff0c;在类加载的时候&#xff0c;就创建了private static MySingleTon mySingleTon new MySingleTon();//创建一个静…