第四章:约束驱动结构 I——物理访问约束
绪论:从算法结构到硬件现实
-
前三章讨论的是 通用数据结构。这些结构主要关注三个问题:
- 数据如何存放
- 如何通过结构压缩搜索空间
- 如何通过计算直接定位数据
这些结构的共同目标是 降低查找复杂度。
-
但在真实系统中,数据结构不仅受到 算法复杂度 的影响,还受到 硬件访问成本 的制约。
-
计算机存储系统具有明显的层级结构:
CPU Cache→主存(RAM)→ 磁盘(Disk) -
不同层级之间的访问延迟差异极大,访问成本可能相差 数十倍甚至数千倍。
因此,在工程系统中,数据结构的设计目标不再只是降低 理论复杂度,还必须 适应硬件访问模型。
-
围绕这些约束,系统逐渐形成了一类新的结构设计:
物理访问约束驱动的数据结构
-
本章讨论四类典型约束:
- CPU Cache 约束
- 内存访问约束
- 磁盘随机 IO 约束
- 磁盘写入模式约束
-
从本章开始,数据结构的设计将不再只由算法复杂度决定,而是由现实系统的 物理访问特性 所驱动。
-
需要说明的是,本章不会逐一罗列所有相关数据结构,而是重点分析 典型结构 与 扩展结构:
- 典型结构:完整展开,说明其核心机制
- 扩展结构:只概括其优化方向与适用场景
一、CPU Cache 约束:缓存局部性
物理现实
- CPU 访问主存时,通常按 Cache Line **(通常约 64B)**为单位加载数据。
- 因此,一次内存访问通常不是只读取一个元素,而是读取其所在的一整段连续区域。
硬件结果
- 连续访问 更容易命中缓存。
- 离散访问 更容易触发 Cache Miss。
- 当结构中存在大量指针跳转时,CPU 很难利用已经加载到缓存中的数据。
工程思路
- 减少指针跳转
- 提高连续存储比例
- 采用块化结构
常用结构
-
块化结构
- 展开链表(Unrolled Linked List)
- 压缩内存数组(Packed Memory Array)
-
分块字符串结构
- 绳索结构(Rope)
- 间隙缓冲区(Gap Buffer)
- 片段表(Piece Table)
典型结构
展开链表(Unrolled Linked List)
核心思路
- 在链表节点中存储 多个元素,减少指针跳转次数。
机制
-
结构
- 块内:连续数组存储多个元素
- 块间:节点通过指针连接
特征
- 缓存局部性提高
- 局部更新成本较低
- 仍保留链表的动态扩展能力
适用场景
- 需要频繁遍历的链式结构
- 需要提高缓存局部性的系统
压缩内存数组(Packed Memory Array)
核心思路
- 在有序数组中 预留空隙,降低插入移动成本。
机制
- 数据存储在连续数组中
- 数组内部保留一定比例的空位
特征
- 连续布局,缓存局部性良好
- 插入成本低于普通有序数组
适用场景
- 内存型有序结构
- 需要兼顾查询效率与更新能力的系统
绳索结构(Rope)
核心思路
- 将长字符串拆分为多个片段,以减少整体字符串复制。
机制
- 叶节点存储字符串片段
- 内部节点维护长度信息
- 整体采用树形组织
特征
- 拼接效率高
- 减少大规模复制
- 适合处理大型文本
适用场景
- 文本编辑器
- 长字符串处理系统
扩展结构
间隙缓冲区(Gap Buffer)
- 优化方向:减少局部编辑成本。
- 核心变化:在数组中维护可移动空隙,使插入操作在局部完成。
- 适用场景:文本编辑器(如 Emacs)
片段表(Piece Table)
- 优化方向:避免原始文本修改。
- 核心变化:通过维护原始文本引用 来表示当前文档状态。
- 适用场景:现代文本编辑器
二、内存访问约束:减少访问路径
物理现实
- 主存(RAM)访问延迟远高于 CPU 运算。
- 数据结构访问路径越长,需要的内存访问次数越多。
硬件结果
- 深层结构会增加访问延迟
- 查找路径越短,访问效率越高
工程思路
- 控制结构高度
- 限制访问路径长度
- 保持动态平衡
常用数据结构
-
平衡搜索结构
- 搜索树(AVL树、红黑树)
- Treap(树堆)
- Splay树(伸展树)
-
分层搜索结构
- 跳表(Skip List)
典型结构
AVL树(AVL Tree)
核心思路
- 通过严格平衡规则限制树高度。
机制
- 任意节点左右子树高度差不超过 1
- 插入或删除后通过旋转恢复平衡
特征
- 查找复杂度稳定
O(log n) - 树高度严格受控
- 更新维护成本较高
适用场景
- 查询频繁,更新较少
- 对延迟稳定性要求较高的结构
红黑树(Red-Black Tree)
核心思路
- 通过 更宽松的平衡规则 降低更新维护成本。
- 本质上是 2-3-4 树(2-3-4 Tree)的二叉表示。
- 一个黑节点及其相连的红节点,可以看作一个 多关键字节点。
机制
-
节点使用 红黑颜色规则 维护平衡
-
在逻辑结构上:
- 黑节点表示树的基本节点
- 红节点表示该节点中的额外 Key
-
插入或删除后,通过 旋转与重新着色恢复结构。
特征
- 查找复杂度
O(log n) - 更新成本低于 AVL 树
- 平衡约束较弱,结构更稳定
适用场景
- 系统容器实现(如有序映射结构)
- 动态数据集合
- 需要频繁更新的有序结构
跳表(Skip List)
核心思路
- 通过多级索引缩短查找路径。
机制
- 底层为有序链表
- 上层为稀疏索引链表
查找从最高层逐级下降。
特征
- 平均复杂度
O(log n) - 结构简单
- 支持并发实现
适用场景
- 内存型索引结构
- 并发系统
扩展结构
Treap(树堆)
- 优化方向:通过随机优先级实现平衡。
- 核心变化:将二叉搜索树与堆结构结合。
- 适用场景:轻量级动态集合。
Splay Tree(伸展树)
- 优化方向:利用访问局部性优化结构。
- 核心变化:访问节点后将其旋转至根附近。
- 适用场景:热点访问明显的系统。
三、磁盘随机 IO 约束:减少磁盘访问
物理现实
- 磁盘读写以 磁盘页(Disk Page / Block) 为单位进行。
- 一次随机访问通常需要 寻道 + 读取整页数据。
因此即使只访问一个元素,系统也必须读取 整个磁盘页。
硬件结果
- 随机访问次数越多,系统性能越低
- 每次磁盘访问都应尽可能读取 更多有用数据
工程思路
- 将节点大小设计为接近磁盘页大小
- 在一个节点中存储大量 Key
- 提高树的扇出,降低树高度
这样一次磁盘读取即可获得 整页索引信息,从而减少访问次数。
常用数据结构
-
多路搜索树:
- B树
- B+树
- B*树
-
缓存自适应结构:
- Cache-Oblivious B-Tree(缓存无关B树)
典型结构
B树(B-tree)
核心思路
- 将搜索树的 单 Key 节点 扩展为 多 Key 节点。
- 一个节点通常对应一个磁盘页,容纳多个 Key,从而一次读取获得更多索引信息。
机制
- 节点内部存储多个 Key、对应数据(或数据引用)以及多个子节点指针。
- 查找时,先在当前节点内部比较 Key,再进入对应子节点。
特征
- 扇出大,树高度低
- 适合磁盘访问
- 插入删除可能触发分裂或合并
适用场景
- 文件系统
- 数据库索引
B+树(B+ Tree)
核心思路
- 在 B树结构基础上,将 数据统一存储在叶节点。
- 非叶节点只存储 索引 Key 与子节点指针。
- 非叶节点可以看作一层 索引结构,用于引导查找路径。
机制
- 内部节点只存储 Key 与子节点指针
- 所有数据存储在叶节点
- 叶节点之间形成 顺序链表(可实现范围查询)
特征
- 树高度更低
- 所有查找路径长度一致
- 支持高效 范围查询
适用场景
- 数据库索引
- 磁盘型范围查询
- B树家族的核心思想是:让一个节点尽可能装满一个磁盘页,从而用最少的磁盘访问完成查找。
扩展结构
B*树
- 优化方向:提高 B+树 节点利用率。
- 核心变化:节点满时优先与兄弟节点重新分配数据。
- 适用场景:高密度磁盘索引结构。
Cache-Oblivious B-Tree
- 优化方向:同时适配不同层级缓存,不依赖固定块大小。
- 核心变化:使用递归布局组织节点,适配不同层级缓存。
- 适用场景:跨层级存储优化。
四、磁盘写入模式约束:顺序写
物理现实
- 磁盘顺序写远快于随机写。
硬件结果
- 频繁随机写会严重影响写入性能。
工程思路
- 将随机写转换为顺序写
- 通过后台线程延迟整理结构
常用数据结构
-
写优化结构
- LSM树
-
LSM变体结构
- Fractal Tree(分形树)
- Bw-Tree
LSM树(Log Structured Merge Tree)
核心思路
- 将随机写转换为顺序写,再通过后台合并恢复有序结构。
机制
-
写入流程:
- 数据首先写入内存结构(MemTable)
- 当 MemTable 达到阈值时,刷写为磁盘文件(SSTable)
- 后台执行 合并(Compaction) ,整理多个 SSTable
-
MemTable 通常采用 跳表或红黑树
-
SSTable 是磁盘上的 不可修改有序表
特征
- 写入性能高
- 顺序写友好
- 读操作可能需要查询多个 SSTable
适用场景
- 日志系统
- NoSQL 数据库
扩展结构
分形树(Fractal Tree)
- 优化方向:降低写放大。
- 核心变化:在LSM基础上,节点中加入缓冲区。
- 适用场景:写入密集型数据库。
Bw-Tree
- 优化方向:提高并发能力。
- 核心变化:使用映射表与追加更新。
- 适用场景:高并发数据库系统。
本章总结
前三章讨论的是 通用数据结构。
而本章开始关注 硬件约束驱动的结构设计。
四类约束对应四类结构演化:
- CPU Cache → 局部性结构
- RAM → 平衡结构
- 磁盘随机 IO → B树家族
- 磁盘顺序写 → LSM结构
此类数据结构的演化,本质上是 硬件访问约束驱动的结构优化。
下一问题
物理访问约束解决的是 硬件访问效率问题。
但在许多系统中,数据结构还需要满足另一类约束:操作顺序约束
例如:
- 任务调度
- 请求处理
- 优先级管理
下一章讨论:操作顺序约束