在本系列的上一篇文章中,我们介绍了 Temporal,一个确定性工作流处理框架,它能以更高的容错性运行有状态的工作流。
当用户将 Temporal 投入生产环境时,一个最重要的决策是确定合适的持久化层。人们常常误认为,只有使用像 Cassandra 这样的 NoSQL 选项才能在 Temporal 中实现扩展。本篇文章将解释为什么 PlanetScale 不需要通过增加运营复杂性来实现扩展性,帮助用户最大限度地利用 PlanetScale 相较于单一 MySQL 实例的扩展性改进。


使用 PlanetScale 分片 MySQL

动画显示一个 keyspace 的 1 个分片 动画显示一个 keyspace 的多个分片
PlanetScale 构建于 Vitess 之上,以利用其通过水平分片实现大规模扩展的能力。Vitess 提供了对 MySQL 的抽象,并通过实现缺失功能来保持关系数据库的优势。
为了直观地了解 PlanetScale 如何提升单一 MySQL 实例的弹性、扩展性和性能,我们可以看看 Vitess 背后发生了什么。

Vitess 的运行时架构

Vitess 是一种数据库技术,它能创建一个单一 MySQL 数据库的假象,而实际上,这个数据库可能由多个分片组成,每个分片都有自己的 MySQL 实例。在分片的情况下,VTGate 层会透明地将查询路由到对应分片。

  • VTGate 是一个无状态的集群入口点,知道所有应用程序逻辑并编码到 vSchema 中。
  • vSchema 是一个抽象层,它呈现底层 keyspace 和分片的统一视图,包含分片表的分片键信息。
  • Vitess 添加了一个随集群规模扩展的辅助进程,也被称为 **VTTablet**,用于队列化连接请求。这一辅助进程从内存管理的角度保护底层 MySQL 进程,允许用户根据需要增加工作线程以扩展应用程序。
  • Shard 是 keyspace 的一个子集,而 keyspace 是逻辑数据库。如果使用分片,一个 keyspace 映射到多个 MySQL 实例;如果不使用分片,keyspace 直接映射到单个 MySQL 实例的数据库。在两种情况下,从应用程序的角度看,keyspace 都表现为单一数据库。

在 PlanetScale 中,所有分片的 keyspace 都有一个 vSchema,并按照 keyspace ID 范围进行分片。keyspace ID 是 Vitess 的内部概念,应用程序无需了解它。要在 PlanetScale 中设置分片,我们会与用户合作,通过 Primary Vindex 配置 vSchema。行的 keyspace ID 在重分片过程中保持不变,因此数据分片数量的变化对应用程序和用户是透明的,无需应用程序更改即可添加更多分片。
如果您对 PlanetScale 和 Vitess 的分片感兴趣,可以参考我们团队最近发布的内容——《一百万 QPS》博客,展示了我们如何预测性分片并处理大量查询。此外,我们还发布了有关连接池和全球路由基础设施的详细内容。


在 Temporal 中的分片

与 PlanetScale 类似,Temporal 通过计算标识符的哈希值实现水平分区,并根据哈希值将其分配到特定的分片。分片用数字 1 到 N 表示。不过,为了与其他持久化层的一致性要求一致,Temporal 序列化了属于同一分片的所有更新,因此所有更新是顺序的。结果是,单个分片的数据库操作延迟限制了其最大理论吞吐量。
在 Temporal 中调整历史分片计数(numHistoryShards)是关键且必须的配置步骤,它会直接影响系统吞吐量、延迟和资源使用。在生产环境中,必须为集群的峰值负载设置足够高的值,这个值在初始部署后是不可变的。
Temporal 的数据库架构设计良好,由于表格之间没有依赖关系,因此在 PlanetScale 中配置 Temporal 数据库的 vSchema 非常简单。大多数 Temporal 表都在主键中定义了 shard_idrange_hash,这直接映射到我们创建 vSchema 时作为分片键的 Primary Vindex。


背后的技术细节

Temporal 是一个写操作密集型应用程序,在更新和插入操作时会轻松累积数 TB 的 bin log。此外,一些表比其他表增加得更快且流量更大。本节将介绍我们如何为某个正在使用 Temporal 分片的客户定义 vSchema 和路由规则。

客户案例

针对该客户,我们为 Temporal 生产工作负载定义了两个 keyspace,其中一个 keyspace 是分片的,另一个是未分片的。两个 keyspace 的存储大小有所不同,但是较大的表放在分片 keyspace 上。
分片的 vSchema 定义如下。我们首先设置 sharded=true 并定义了我们所需的预定义 Vindexes(如 xxhash)。接着使用 MoveTables 过程将表移动到新的分片 keyspace,并使用 SwitchTraffic 命令手动切换流量。分片 keyspace 的 ColumnVindex 定义了表的分片键(如 shard_idrange_hash),因此我们在 ColumnVindex 中指定了列名及哈希函数。


结论

PlanetScale 基础设施团队最近已在内部实施 Temporal 工作流,用于 Vitess 发布的自动化任务。此外,我们的部分客户也在生产环境中使用 Temporal 工作流,并实现分片。
在最近的节假日销售高峰(黑色星期五和网络星期一)期间,我们的一位客户在没有中断的情况下实现了创纪录的 QPS 和 IOPS。针对 Temporal 数据库的峰值负载流量,我们的解决方案实现了对节点故障的影响减小,同时显著增加了读写吞吐量。通过分片,可以将读写操作分布到多台机器上,从而实现数百万 QPS。以前为了扩展性而牺牲操作简化的日子已经过去了。



规模化处理 Temporal 工作流:第二部分——生产环境中的分片插图

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:http://www.choupangxia.com/2025/09/10/temporal-2/