跳到内容

第 5 卷 / 第 14 章 / 第 1 课

14.1 内存优先数据库:执行、布局与恢复 ​


元数据卡

  • 前置:第 4 章存储与恢复、第 13 章并发控制
  • 关键词:内存优先、CPU Cache、分区执行、日志、快照、恢复
  • 案例:H-Store/VoltDB、SAP HANA、DuckDB

“数据在内存里”可能只表示缓存命中,也可能表示系统从数据布局、并发控制到恢复路径都以 DRAM 常驻为前提。两者不能混为一谈。

先分清三类系统 ​

类型权威状态放在哪里内存不足时怎样处理典型关注点
磁盘优先数据库持久化页/段,内存是缓存驱逐页并从存储重读Buffer Pool、随机 I/O、WAL
内存优先数据库主工作集以可直接执行的内存布局存在容量规划、分区、冷数据分层或拒绝写入Cache/NUMA、日志吞吐、恢复时间
可溢写分析引擎内存做算子工作区,数据可在持久化文件或外部对象存储算子 spill、重新扫描或分块向量化、内存预算、临时 I/O

DuckDB 属于嵌入式分析数据库,支持 buffer manager 和 larger-than-memory 查询,不应称为“用操作系统 mmap 代替 Buffer Pool 的纯内存库”。Redis 更接近内存数据结构服务;HANA、VoltDB 一类系统则从不同方向探索内存优先的数据管理。

去掉磁盘缺页,并不等于没有存储成本 ​

把一次访问固定写成“磁盘 10 ms、内存 100 ns,因此快十万倍”会误导设计。真实延迟还受设备、队列深度、页缓存、网络、访问模式和命中率影响。数据常驻内存后,瓶颈会转向:

  • CPU cache miss 与指针追逐;
  • NUMA 远端内存访问;
  • 内存带宽和分支预测;
  • 索引、版本、对象头及碎片带来的容量放大;
  • 日志刷盘、复制确认和网络往返;
  • 热点键上的串行化与排队。

所以内存数据库仍需要执行计划、紧凑布局和并发控制。一个选择性很差的查询不会因为少了磁盘缺页就自动变好。

内存布局可以围绕 CPU 设计 ​

磁盘页式系统倾向用稳定页号和槽位定位记录。内存优先系统可以采用更紧凑的数组、偏移或指针,但直接指针并非免费:重启后地址失效,压缩和移动对象更困难,复制快照也需要重新定位。

设计时应比较:

  • 行布局适合点查和更新,列布局适合投影、压缩与扫描;
  • 固定宽度访问简单,变长字段需要额外区域或间接引用;
  • 节点式索引更新方便但 cache locality 较差,紧凑数组更适合批处理;
  • 每行版本和锁元数据会影响实际可容纳数据量。

“无 Buffer Pool、直接指针、编译执行”不是所有内存系统共同具备的三条定律,而是可选设计空间。

分区串行执行:用数据归属消掉局部协调 ​

H-Store/VoltDB 路线把数据按分区归属到执行站点。单分区事务在所属执行上下文中按确定顺序运行,可避免该分区内部大量锁闩开销。

收益依赖一个关键前提:事务的大部分读写都落在同一分区。若分区键选择不当:

  • 热门分区限制总吞吐;
  • 跨分区事务需要协调多个执行站点;
  • 分区迁移和再平衡变得昂贵;
  • 长事务阻塞同分区后续工作。

“单线程”也不是整个集群只有一个线程,而是特定分区的事务执行被串行化;网络、日志、复制和其他分区仍可并行。不要据此宣称系统完全没有并发控制。

持久化必须同时回答 RPO 与 RTO ​

内存是工作状态,不代表可以省略持久化。常见组合是:

  1. 事务日志记录恢复所需变化或确定性命令;
  2. 周期快照缩短日志重放区间;
  3. 复制降低单机故障风险;
  4. 备份覆盖误删、逻辑损坏和整个故障域丢失。

需要明确两类目标:

  • RPO:故障时允许丢多少已接收数据;
  • RTO:从快照、日志和副本恢复服务要多久。

只说“有快照”没有说明最近一次快照后的数据窗口;只说“有副本”也没有说明副本是否同步、是否落盘,以及故障切换会不会丢失已确认写入。

恢复路径应定期演练 ​

内存数据集越大,重启加载越可能成为可用性瓶颈。恢复预算应包含:

text
发现故障
+ 选主或重建实例
+ 读取基础快照
+ 重放增量日志
+ 重建非持久化索引/缓存
+ 追上复制或消息流
+ 校验后开放流量

假设快照 128 GiB,实际恢复读取速率为 1 GiB/s,随后还需重放 800 万条记录,回放速率为每秒 20 万条。在两阶段不重叠的假设下,单是加载与重放就需约 128 + 40 = 168 秒;还没计入选主、校验与开放流量。原始数据能放进 RAM,并不能让重启跳过这 168 秒。

日志写得快不代表恢复也快。应该用接近生产数据量的备份定期测试恢复耗时、校验方法和故障切换脚本。

DuckDB 与 HANA 放在这里怎样理解 ​

DuckDB 是嵌入式 OLAP 引擎,采用向量化执行和自己的 buffer manager,可在内存预算下把部分中间状态溢写到磁盘。部分聚合状态和算子组合仍可能超出预算或遇到 spill 限制,不能据此承诺任意查询都不会 OOM。它说明“为内存效率优化”与“数据必须全部放内存”是两回事。

HANA 等内存优先列式系统仍有日志、保存点和持久化层:重启可从完成的保存点读取数据,再回放此后的 redo。参见 SAP HANA 数据与日志卷。内存中的主工作布局服务低延迟访问,持久化层服务恢复;这也不是“磁盘只做偶尔备份”那么简单。

选型检查表 ​

  • 工作集和峰值内存放大是多少,而不只是原始数据量?
  • 热点能否按稳定业务键分区?跨分区事务比例多高?
  • 提交确认要求到本机日志、同步副本还是多故障域?
  • 冷启动与全量恢复是否满足 RTO?
  • 超过内存预算时系统 spill、淘汰、拒绝写入还是崩溃?
  • 是否真的需要完整 SQL、约束和多行事务?

参考 ​

下一节聚焦 Redis:它的原子命令、MULTI/EXEC、RDB 与 AOF 分别能保证什么。

Built with VitePress | Software Systems Atlas