【华为云 MySQL 技术专栏】TaurusDB 存算分离,SAL 组件的妙用

1. 背景介绍
TaurusDB 是一种采用存储与计算分离架构的云原生关系型数据库。相对于传统的 MySQL 主备架构,TaurusDB 基于共享存储,具有更高的可用性、更高性能、更低复制延迟,以及快速的弹性扩展能力。
如图 1 所示,TaurusDB 架构从上到下分为 3 个部分:计算层、存储抽象层(Storage Abstraction Layer, 简称 SAL)和存储层。

图 1 TaurusDB 架构示意图
计算层主要由一个主节点(Master)和多个只读节点(Read Replica)组成。主节点负责提供业务的读写能力,而只读节点则用于分担读取负载,提高系统的整体性能和可用性。
存储层包括两个主要的存储子服务:Log Store 和 Page Store。其中,
- Log Store:用来持久化日志的服务。
- Page Store:提供一致性多版本页面能力的存储服务。
为了便于管理页面数据,我们把多个 Page 数据进行分片,每个分片(Slice)固定大小为 10G。
SAL 层位于计算层的 InnoDB 引擎和存储节点的中间层,主要给 InnoDB 层提供日志和数据的存储服务。本文将详细介绍 SAL 层的主要功能,及其与上下层的交互。
2. SAL 层主要功能
SAL 层包括位于计算节点部分的 SAL-SQL 层和存储节点的 Page Store,其主要功能主要包括日志服务、管理页面数据、数据写入、页面读取和后台任务这 5 部分。
2.1 日志服务
SAL 层的第一个主要功能就是日志服务,用于实现 Append-only 模式写日志和读日志能力。
Log Store 提供一个基本的 Append-Only 的存储对象,即 PLog(Persistent Log,持久化日志)。每个 PLog 的大小是固定的,且可以配置副本同步策略。例如,TaurusDB 目前会配置单个 PLog 的大小为 256M,并且采用 3 副本强一致策略,也就是说,写入请求需要在 3 个副本上都完成,才视为成功。
Log Store 通常由多个服务器组成。如果 PLog 的某个副本所在服务器出现异常,那么写入请求会立刻选择新的服务器,并在该服务器上创建新的 PLog,然后再次执行请求,直至成功。
在此期间,正在写入的 PLog 是 active 状态,而当 PLog 出现异常或者写满后不再使用的 PLog 时,会将其设置为冻结状态(sealed)。
基于基础的 PLog,SAL 层实现了 CULog(Continous Unlimited Log,无长度限制日志),提供了一个具有无限空间的日志组件。
数据库日志系统,使用多个 CULog 进一步封装实现了日志对象 PWAL(Persistent Write Ahead Log,持久化预写日志)。PWAL 具有出色的写入和持久化能力。

图 2 PWAL 结构示意图
如图 2 所示,PWAL 包括多个日志 CUlog 和一个 Index CULog。目前 TaurusDB 配置 8 个日志 CULog,每次日志写入时,采用轮询的方式,并发写入日志 CULog,以提高性能。
每条日志对应一个递增的日志序列号(LSN,Log Sequence Number),SAL 维护多种 LSN。其中,写入 Log Store 的连续位点,叫做 flush_to_disk_lsn。
日志系统持续写入数据,当老的日志完全不再被使用时,后台清理线程则会定期清理并释放空间。
2.2 管理页面数据
SAL 层的第二个功能就是管理页面数据,即管理 Slice,这一功能由 SliceManager 模块实现,主要维护 InnoDB 的<space id, page number>和 Slice 之间的映射关系。
其中,Slice 作为数据分片,每个分片大小为 10GB,而 SAL 层会按照更小的粒度(Segment)进行分配和管理,每个 Segment 大小为 4MB。
SliceManager 主要包括以下数据结构:
- SliceTable:维护所有 Slice 的记录;
- TablespaceMap:维护 spaceId 和 TableSpace 对象之间的映射关系;
- SegmentTable:每个 TableSpace 有一个,用来维护 Segment 和 Slice 的映射关系。
SliceManager 最常见的一种用户场景是这样的:
当一张用户表插入数据时,InnoDB 层发现需要扩充表空间,以便分配出新的空闲页面。此时,通过 SliceManager,找到一个有足够空间的 Slice,并分配一个空闲的 Segment。
对 Slice 的操作需要持久化,因此 SliceManager 的操作会记录到一个日志文件中(使用 CUlog 来实现)。为了避免日志量较大,导致恢复耗时增多,我们也会采用 Checkpoint 机制,将 SliceManager 的日志 checkpoint 到 CPR CULog 中。CPR 日志的内容,包括当前的 TableSpace、Slice 和 Segment 信息,这些信息会被序列化并存储。
2.3 数据写入流程
SAL-SQL 层中 CLP(Common Log Process,通用日志处理)是提供日志处理能力的核心模块。为了高效地处理日志写入,CLP 将整个写入链路拆分为多个子阶段,以一种 PipeLine(流水线)的模式运转,写入流程如图 3 所示。

图 3 TaurusDB 写入流程示意图
首先,用户操作数据库时,会产生页面修改的日志;SAL 层通过日志组件,将日志写入 Log Store 3 副本,一旦 3 个副本都成功写入(图中步骤 3),则认为写入完成,然后返回 InnoDB 层,事务提交或者继续执行。
接下来,SAL 层将日志按照 Slice 进行整理和合并,当满足以下条件之一时,发送到对应的 Page Store:
- 缓存日志的 Buffer 已满;
- 达到刷新日志的时间间隔(步骤 4)。
Page Store 采用最终一致的副本同步策略,3 副本中只要一个副本返回成功,就可以认为请求成功。SAL-SQL 收到请求响应后,就可以回收日志 buffer 并继续处理后续请求。
此外,Slice 的 3 个副本之间也会通过 Gossip 机制,进行数据同步。
2.4 页面读取
Page Store 是存储层提供页面读取的组件。当 Page Store 接收到 SAL-SQL 层发送的日志后,后台持续应用这些日志,以便生成最新的页面。
计算层 Master 节点通常只需要从存储层获取新版本的数据,而 Read Replica 节点则可能会读取较旧版本的页面数据。具体来说,ReadPage 接口中会指定所需版本的 LSN(Log Sequence Number)。
SAL 层会维护每个 Slice 的最新日志位点,这样,SQL 层页面读取请求可以携带该目标 LSN(desired LSN)。SAL 层会根据这个 LSN 选择合适的 Page Store 副本去获取页面。如果某个副本失败,会切换到其他副本后重试。
同时,Page Store 需要保留多个版本的页面数据,这会占用一定内存和磁盘资源。为了尽快回收这些资源,SAL-SQL 层也需要定期通知 Page Store 可以回收的日志位点。
2.5 后台任务
Log Store 日志回收
SAL 维护每个 Slice 的 3 副本的 persist LSN。当所有 Slice 的最小 persist LSN 确定后,这就是 Page Store 所需的最小位点。
如果其他功能组件(如增量备份)也不需要 Log Store 的日志,那么 Log Store 中这部分日志就可以回收了。
我们通过 truncate_lsn 来维护可以 truncate 的位点。
Page Store 的页面 Consolidation
Page Store 收到日志后,会解析并在 LogDirectory 组件中保存其索引信息。当处理读页面请求时,需要读取并回放多个日志,才能获取对应版本的页面。
通常情况下,Master 节点是要读取最新版本的页面,这要求处理到最新日志,从而导致读取时延高。
为了解决这一问题,通过后台的 Consolidation 任务,会尽快推进页面回放到最新版,提高读取效率。
Page Store 的页面 Compaction
随着业务持续运行,Page Store 存储的页面数据也会持续失效和变化,底层存储的数据文件也出现碎片。
为了提高空间利用率,Page Store 会启动后台 Compaction 任务,持续分批次处理碎片率较高的文件,并进行整理和合并。
3. SAL 层的 Log Router 组件功能
SAL 层的 Log Router 组件负责从多种源收集、处理和路由日志数据,支持日志解析、过滤和转换,确保日志的实时性和可靠性。
3.1 Log Router 组件部署示意图
TaurusDB 的实际部署图,如图 4 所示。在计算层的一台物理机上,会部署多个数据库节点,每个节点都运行在独立的容器中,以实现 CPU、内存等资源隔离。

图 4 TaurusDB 部署示意图
每台物理机上,TaurusDB 还部署一个轻量级的 Log Router 组件,用来承接各个容器内的数据库进程和存储池之间的网络交互请求。
引入该组件有两方面的好处:
1)集中处理网络请求。
所有节点的网络请求都会集中处理,通过单一功能的高内聚设计,可以高效地利用资源。
2)解耦存储层和数据库逻辑。
Log Router 维护存储池的节点路由信息,并感知其变化,进一步将存储层的逻辑和容器内的数据库逻辑解耦,减少上下层的复杂性。
3.2 Log Router 平滑升级
多个数据库节点共享 Log Router。当进行 Log Router 组件升级时,TaurusDB 通过一种平滑升级机制,从而实现用户无损升级。其主要流程如图 5 所示:

图 5 Log Router 平滑升级示意图
1)预检查。
物理机上 Agent 组件执行升级前预检查工作,分别检查同机的所有数据库节点;
2) 下发排干命令。
升级程序执同时向所有数据库进程下发命令。数据库进程会在 SAL 层执行排干动作,短暂阻塞读写请求,避免出现请求丢失;
3) Log Router 升级。
接着,可以安全的进行 Log Router 组件升级,Router 进程启动后就可以提供服务;
4) 恢复请求。
升级程序向同机所有数据库进程下发解除命令,恢复第二步中被阻塞的 SAL 层请求。
至此,整个流程完成,各个数据库节点和业务无损。
4. 总结
SAL 组件作为适配层,位于计算层的 InnoDB 引擎和存储节点之间,具备数据库的日志处理能力,负责页面和存储之间的数据映射管理及页面读写操作。当存储引擎将请求发送到 SAL 组件后,SAL 负责与存储节点进行交互,简化了 InnoDB 引擎和存储节点之间的复杂交互。
此外,SAL 组件还提供了高级的数据管理和优化功能,如数据压缩和缓存机制,能够有效减少存储空间的占用,并加快数据访问速度,进一步提升系统的整体性能。这种设计不仅提高了系统的可扩展性和可靠性,还降低了运维成本,为大规模数据库应用提供了强有力的支撑。
评论