理解存算分离数据库的设计原则和性能影响

阅读 SIGMOD 2024 一篇来自 Purdue University 的论文,关于存算分离数据库,做一些关键信息的摘要记录

简介

存算分离架构在云数据库中广泛使用,包括 Amazon Aurora/MicroSoft Socrates/Google AlloyDB/Alibaba PolarDB/Huawer Taurus。
传统非存算分离的架构,会把数据库的存储和计算聚合在同一物理机器上;而存算分离架构下,计算通过网络来访问存储,这样的设计能够独立去扩容计算或存储,在提升资源利用率、降低成本、故障快速恢复上有优势。
存算分离数据库通常会满足以下设计原则:

设计原则 P1:存储计算引擎隔离

  • 存储引擎(包括 Logging 和 Storage)和计算引擎(包括 SQL Layer、Buffering、Transaction)部署在不同的物理节点
  • 这种分离架构核心是把存储访问变成远程/共享存储,如果没有缓冲层,远程访问的性能会非常差

设计原则 P2:Log as the Database(LogDB)

  • 为了降低计算引擎-存储引擎之间的网络开销,除了适用 Buffering,存算分离架构通常会引入 Log as the Database 设计
  • 仅在事务提交时把 WAL 同步到存储层,而不去同步数据 Page,减少网络上需要传输的数据
  • 存储引擎层通过异步回放 WAL 来获得真实的数据 Page

设计原则 P3:Shared-Storage 架构

  • Shared-Storage 是相对于 Shared-Nothing 来讲的,指不同的计算引擎共享一份存储引擎数据,减少拷贝和异动数据
  • 因为主从计算节点的同步延时,从节点可能需要读取老版本数据,Shared-Storage 需要支持 Multi-Version Pages

这里明确几个实现上的细节:

  • 讨论 P2 LogDB 的时候,通常是做了 P1 存储计算引擎分离
  • 讨论 P3 Shared-Storage 的时候,通常是做了 P1 存储计算引擎分离和 P2 LogDB

架构实现

XLog 是 PostgreSQL 中的 WAL

单体架构

下图是 PostgreSQL(v13.0) 的架构,数据库整体跑在单个节点上

远程盘

  • 存储引擎和计算引擎拆分到不同的节点上,之间走网络通信
  • 读写流程和单体架构没区别,核心是本地读写改成远程读写
  • 远程盘的优势是独立扩容,其次是 LogDB 架构的基础

Log as the Database(LogDB)

  • 远程盘因为网络开销表现会比较差,优化一方面是引入 Buffering,其次就是 LogDB
  • 写路径在事务提交阶段,把 WAL 发送给存储节点,实际的数据 Page 通过在存储节点异步回放 WAL 生产(Step a/b)
  • 读路径计算节点先检查 Local Buffer,Cache Miss 时从存储节点加载 Page 数据,如果此时 Page 数据尚未完成回放,会同步开始回放(Step1)
  • 传统数据库也会有异步刷脏的设计,仅从架构角度不能说 LogDB 传输 Log 而非 Page 的方式,一定比单体架构性能高

多版本 LogDB(LogDB-MV)

  • Shared-Storage 架构下不同计算节点共享同一个存储层,假设此时是单 Primary-多 Secondary 计算节点
    • Primary 节点支持读写事务
    • Secondary 节点仅支持只读事务
  • Primary 把数据更新异步同步给 Secondary,因为延时 Secondary 可能读取老版本 Page,这个称为 GetPage@LSNmailto:GetPage@LSN
  • 存储节点回放 WAL 时保存 Page 的多个版本
    • 存储引擎维护 VersionMap,PageID-LSM 的映射(Step a)
    • WAL 会拆分程 miniWAL,每个 miniWAL 仅包含一个 Page 的修改,回放阶段会把多个 Page 数据用 PageID+LSN 作为 Key 插入 RocksDB
    • GetPage@LSNmailto:GetPage@LSN 请求首先同步等待回放进程完成读请求 LSN 的处理,然后从 VersionMap 中获取指定 PageID 的 Version LSN 列表, 从 RocksDB 中把小于等于请求 LSN 的 Page 数据加载出来,正常最终的 Page 数据
  • 多版本的支持增加了 RocksDB 的写压力

为了加速读路径,LogDB-MV 提供 Filtered Replay 和 SmartReplay 两种优化思路。

  • FilteredReplay:GetPage@LSN 阶段,通过 QuickScan 跳过和当前 Page 无关的 LSN,加速读
  • SmartReplay:GetPage@LSN 阶段只去回放 Page 相关的 LSN,不同 Page 的回放可以多进程并行

测试数据

测试设置

  • 计算节点 x 16(1 写 15 读)
    • 写节点配置:Intel Xeon Gold 6330 CPU(2.0GHZ), 250GB DRAM, 1.5TB NVMe SSD
    • 读节点配置:Intel Xeon Silver CPU (2.3 GHz), 64GB DRAM, and a 900GB NVMe SSD
  • 存储节点 x 3(也测试了一版 6 存储节点,模拟 Aurora 架构)
    • 节点配置:Intel Xeon Platinum 8368 CPU(2.4GHZ), 188GB DRAM, 1.5TB NVMe SSD
  • 测试环境:Ubuntu 22.04, 10Gb TCP/IP 网络
  • 测试数据:SysBench 和 TPC-C
    • SysBench:2000 张表,每张表 20w 行数据,整体数据库大小 96GB

读写性能

单体架构 vs 远程盘

  • 计算节点 Buffer 越大,读性能越高
  • 计算节点 Buffer 超过 700MB 后,增加 Buffer 对写性能提升不大
    • 主要是生成的脏页在 700MB 左右
    • 在 Heavy workload 下,这个阈值会稍高
  • 写性能在远程盘情况下,劣化严重

远程盘 vs LogDB

  • 读性能相当
  • LogDB 在写性能上优化明显,特别是 Heavy workload 下最大提升 2.5X(8GB buffer)
  • Ligh workload 下,两种架构写性能相当,原因是低负载下有足够的时间去异步刷脏,同时也意味着 LogDB 本身并没有提升写性能
  • Heavy workload 下,远程盘架构刷脏吞吐不够会阻塞写请求,而 LogDB 没有刷脏进而获得吞吐提升

写后读场景

  • 持续 5min 写入
    • LogDB 落后远程盘 20.3%
    • LogDB-MV 落后远程盘 66.2%
    • Gap 主要是回放日志的开销

LogDB vs. LogDB-MV

  • Multi-Version 主要影响写,不影响读性能
    • 一条 WAL 对应多个 Page,都要插入 RocksDB
  • 移除写放大问题,Multi-Version 的写性能提升 37%

读性能水平扩展

  • 水平扩容计算节点提升吞吐

FR vs. SR

  • 只读请求不受 FR/SR 影响,优化混合读写场景
  • FR 大幅度减少 LSN 回放,相比 LogDB-MV 写吞吐提升 50%