谷歌诞生于云端。在 Google,我们庞大的基础架构为内外的服务提供了20多年的支持。我们持续研发基础架构,无论是用户可见的还是不可见的,使基础设施更加高效、可靠和安全是我们的目标。基础设施不断更新和改进。为全球数十亿用户提供服务,可用性和可靠性是我们运营和更新基础设施的核心。
Spanner 是 Google 的大规模可扩展、可复制且高度一致的数据库管理服务。在我们的生产实例中运行着数十万个数据库,Spanner 在峰值时每秒处理超过 20 亿个请求,并且管理着超过 6 EB 的数据,这些数据是许多关键服务的“事实来源”,包括 广告服务、搜索和 Cloud Spanner 。客户的工作负载是多种多样的,并且会以各种方式扩展系统。尽管 Spanner 不断发布二进制版本,但更换底层存储引擎等根本性变化是一项具有挑战性的任务。
在这篇文章中,我们将讨论将 Spanner 迁移到新的列式存储引擎Ressi的过程。我们讨论了大规模迁移所面临的挑战,以及我们如何在大约 2-3 年内完成这项工作,并且所有关键服务都在上面不间断地运行。
存储引擎是实际存储数据的地方。在 Spanner 部署中,数据库托管在一个或多个实例配置中,这些实例配置是资源的物理集合。实例配置和数据库包含一个或多个zone或副本,由多个 Spanner 服务器提供服务。服务器中的存储引擎对数据进行编码,并将它们存储在底层的大规模分布式文件系统Colossus中。
Spanner 最初使用基于 SSTable(Sorted String Table)技术的类 Bigtable 的存储引擎。经过多年的大规模部署(例如在 Bigtable 和 Spanner 本身中),这种格式已被证明非常强大。SSTable 格式针对主要由大字符串组成的无模式 NoSQL 数据进行了优化。虽然它是 Bigtable 的完美匹配,但它并不是 Spanner 的最佳匹配。特别是,遍历单个列是低效的。
Ressi 是 Spanner 的新的low-level、面向列的存储格式。它从头开始设计用于处理具有混合 OLTP 和 OLAP 工作模式的大规模分布式数据库上的 SQL 查询。包括维护和提高读写数据库中键值数据的性能。Ressi 对块级数据布局、活动和非活动数据的文件级组织以及存储 I/O 节省的过滤器等方面进行优化。数据组织提高了存储使用率并有助于大型扫描查询。在 Spanner 上部署 Ressi 和非常大规模的服务(例如 GMail)已经显示出在多个维度上的性能改进,例如 CPU 和存储 I/O。
Spanner 的改进和更新是不断的,我们擅长在动态环境中安全地操作和发展我们的系统。然而,更换存储引擎改变了数据库系统的基础并提出了明显的挑战,尤其是在大规模部署的时候。
迁移的最高要求是在整个迁移过程中保持服务的可靠性、可用性和数据完整性。由于 Spanner 的大规模部署,这些挑战是最重要且独一无二的:
迁移到新存储引擎的另一个挑战是迁移之后性能提升以及成本降低。由于上层应用与新的存储引擎交互,在底层数据迁移期间和/或迁移之后可能会出现性能衰退。这可能会导致延迟增加和请求被拒绝等问题。
由于数据库之中压缩方法的差异,性能衰退也可能表现为某些数据库中存储使用量的增加。这增加了内部资源消耗和成本。更重要的是,如果没有额外的存储空间,可能会导致生产中断。
虽然新的列式存储引擎总体上提高了性能和数据压缩,但由于 Spanner 的大规模部署,我们必须注意异常值。
双存储系统的同时存在不仅需要更多的工程努力来支持,而且还会增加系统复杂性和不同区域的性能差异。减轻这里风险的一个明显方法是实现提高迁移速度,特别是缩短同一数据库中双重格式的共存时间。
然而,Spanner 上的数据库有不同的大小,跨越几个数量级。因此,迁移每个数据库所需的时间可能会有很大差异。为迁移调度数据库不能一刀切。迁移工作必须考虑到存在双格式的过渡期,同时尝试安全可靠地实现最高速度。
我们引入了一种自定义的可靠性原则。使用可靠性原则,我们的自动化框架自动评估迁移候选者(即实例配置和/或数据库),选择符合条件的候选者进行迁移并标记非法行为。对标记的迁移候选者进行了专门检查,并在候选者获得移民资格之前解决了非法问题。这在不牺牲生产安全的情况下有效地减少了工作量并提高了速度。
可靠性原则是我们进行迁移的基石。它们涵盖了多个方面:
在这个架构设计中,可靠性原则变成了过滤器,只有满足要求的迁移候选者才能通过并被迁移调度程序选择。迁移计划每周进行一次,以实现平滑过度。
如前所述,不满足可靠性原则的迁移候选不会被忽略 - 它们会被被标记为注意并以两种方式之一解决:
迁移调度器是管理迁移风险、防止性能下降和确保数据完整性的核心组成部分。
由于客户工作负载的多样性和部署规模的广泛性,我们采用了细粒度的迁移调度。调度算法将客户部署视为故障域,并适当地分阶段和间隔客户实例配置的迁移。与迁移自动化一起,他们实现了高效的迁移过程,同时控制了风险。
在此框架下,迁移在以下几个方面逐步进行:
在客户部署中逐步迁移要求我们将客户部署识别为故障域。我们使用了部署所属和使用情况的启发式方法。在 Spanner 的案例中,这与上层应用是类似的,因为多个实例通常是同一服务的区域实例。该分类产生了等效的部署实例类,其中每个类都是来自同一客户且具有相同工作负载的实例配置的集合,如简化图所示:
周级调度器从每个域中选择迁移候选者(即实例配置中的副本/区域)。可以独立选择来自多个域的候选人,因为他们的工作是隔离的。
在上述机制下,客户部署通过一系列状态更改小心地执行,防止性能下降以及可用性和数据完整性的损失。
开始时,选择客户的实例配置并迁移初始区域/副本(以下称为“第一个区域”)。这避免了对客户的潜在全局生产影响,同时揭示了上层应用与新存储引擎交互不佳时的问题。
在第一次区域迁移之后,通过使用 Spanner 的内置完整性检查将迁移的区域与其他区域进行比较来检查数据完整性。如果此检查失败或迁移后发生性能下降,则实例将恢复到之前的状态。
我们预先估计了迁移后的存储大小,可靠性原则阻止了迁移后存储增加过多的实例。因此,我们在迁移后并没有出现很多意外的存储压缩回归。无论如何,我们的监控基础设施密切监控资源使用情况和系统健康状况。如果发生意外回归,则通过将区域迁移回 SSTable 格式将实例恢复到所需状态。
只有当一切正常时,客户部署的迁移才会继续进行,逐步迁移更多实例和/或区域,并随着风险的进一步降低而加速。
大规模的迁移工作需要有效的项目管理和确定关键指标以推动进展。我们推动了一些关键指标,包括(但不限于):
执行大规模迁移是一项包括战略设计、自动化、设计流程以及随着工作进展而转移执行方案的工作。通过系统化和有原则的方法,我们在短时间内实现了大规模迁移,涉及管理的超过 6 EB 数据和 20 亿 QPS 在 Spanner 中的峰值,并且服务可用性、可靠性和完整性不受影响。
Google 的许多关键服务都依赖于 Spanner,并且已经通过这次迁移看到了显着的改进。此外,新的存储引擎为许多未来的创新提供了一个平台。以后会越来越好。