Skip to content

第三章:存储与检索

1. 存储与检索的核心问题

数据库底层目标可以归结为两件事:

  • 写入路径:如何高效、可靠地把数据落到持久化介质。
  • 读取路径:如何在可控延迟内找到目标数据。

索引是两者之间的典型权衡:加速读,增加写放大与存储开销

2. 基础存储结构

2.1 追加日志与哈希索引

最简单的 KV 存储是追加日志:写入顺序追加,读取反向扫描。为避免 O(n) 读取,需要内存哈希表维护 key -> offset

该模型写入快,但有两个明显限制:

  • 所有键通常要常驻内存。
  • 范围查询能力弱。

2.2 SSTable 与 LSM-Tree

SSTable 要求键有序。LSM-Tree 一般由 MemTable、磁盘层级文件、后台压缩组成:

  1. 写入先到内存有序结构,再顺序刷盘。
  2. 读取按新到旧查找多个层级。
  3. 后台 compaction 合并并清理旧版本。

关键配套:

  • WAL:崩溃恢复。
  • Bloom Filter:快速判定“键不存在”,减少无效磁盘查找。

2.3 B-Tree 及其变体

B-Tree/B+Tree 以页为单位组织数据,读写路径短、范围查询稳定,是关系数据库主流索引结构。

典型特征:

  • 更新常为随机写,需配合 WAL 保证崩溃一致性。
  • 页分裂与合并维护树平衡。
  • 并发依赖锁存器与多版本机制。

2.4 LSM-Tree 与 B-Tree 对比

维度 LSM-Tree B-Tree
写入吞吐 高,顺序写友好 中等,随机写较多
读取放大 可能查多层文件 路径稳定,点查快
范围查询 可做,但常弱于 B+Tree 强项
空间与压缩 压缩效果通常更好 依赖页利用率
延迟稳定性 压缩期可能抖动 相对平稳

3. 索引设计

3.1 主键索引与二级索引

  • 主键索引决定数据主存放路径。
  • 二级索引用于非主键条件检索,但会引入额外写放大与一致性维护成本。

3.2 聚簇、覆盖与多列索引

  • 聚簇索引:数据行与索引叶子同址,主键查找高效。
  • 覆盖索引:索引包含查询所需列,减少回表。
  • 多列索引:遵循最左匹配与查询模式设计,不可盲目叠加。

3.3 全文与模糊检索

全文搜索通常使用倒排索引,核心优化点是分词、倒排压缩和相关性排序。若查询以文本相关性为主,建议由专用搜索引擎承担。

4. OLTP 与 OLAP

4.1 两类负载差异

  • OLTP:高并发、小事务、低延迟、点查为主。
  • OLAP:大扫描、聚合计算、吞吐优先。

同一存储结构很难同时对两类负载最优,这也是数据仓库与湖仓体系出现的根本原因。

4.2 数据仓库与建模

ETL/ELT 将业务库数据汇总到分析系统,常见模型:

  • 星型模型:事实表 + 维度表,结构清晰,查询友好。
  • 雪花模型:维度进一步规范化,存储节省但查询更复杂。

5. 列式存储

5.1 为分析而生

分析查询通常只访问少量列,列式存储可显著降低 I/O。列内数据同质,天然适合压缩与向量化执行。

5.2 列压缩与向量化

常见技术包括字典编码、位图、游程编码。配合 SIMD 指令,CPU 可一次处理多条数据,提高扫描效率。

5.3 列存写入策略

列存更新成本高,工程上常采用“增量写入 + 后台合并”,本质上与 LSM 思路一致。复杂聚合可通过物化视图预计算换取查询时延。

6. 实务建议

  • 先根据查询模式选引擎,再做索引设计。
  • 高写入日志类场景优先评估 LSM-Tree
  • 高并发事务与范围查询优先评估 B+Tree
  • 分析链路优先列存与声明式计算引擎。