在深入阅读 H2 数据库源码时,会发现内部存在两套完全独立的存储实现——
MVStore和PageStore。初次接触时容易混淆:同名类
FileStore为何分布在不同包下?MVMap和Page又是什么关系?本文通过层级对照的方式,将两条并行的存储栈拉平对比,帮助建立起清晰的结构认知。
MVStore为默认引擎,采用MVCC机制;Regular对应传统的PageStore栈。
背景
Database 在启动时根据配置决定走哪条存储栈,MVStore 为当前默认选项:
// @see org.h2.engine.Database#open
// 依据配置 MV_STORE 决定使用哪套引擎
if (dbSettings.mvStore) {
// 走 MVStore 栈(新)
} else {
// 走 PageStore 栈(旧)
}
🎈 两套栈互不混用,表引擎、存储层、文件 I/O 全是独立实现。理解这一点,就不会再把 org.h2.mvstore.FileStore 和 org.h2.store.FileStore 搞混了。
两条并行的存储栈
graph TB
DB["Database 数据库引擎"]
DB -->|MVCC/默认| MVStack["MVStore 栈"]
DB -->|传统| PSStack["PageStore 栈"]
subgraph MVStack["MVStore 存储栈 (新)"]
MVTable["MVTable extends TableBase"]
MVTable --> MVStore["MVStore 存储引擎"]
MVTable --> TxStore["TransactionStore 事务层"]
MVStore --> MVMap["MVMap 有序 K-V 树"]
MVStore --> MVFileStore["mvstore.FileStore"]
MVMap --> MVPage["mvstore.Page"]
end
subgraph PSStack["PageStore 存储栈 (旧)"]
RegularTable["RegularTable extends TableBase"]
RegularTable --> PageStore["PageStore 存储引擎"]
PageStore --> PSFileStore["store.FileStore"]
PageStore --> PSPage["store.Page 固定页"]
end
⚠️ 关键点:
org.h2.mvstore.FileStore和org.h2.store.FileStore是两个不同的类,分属两套栈,互不相关。
存储实现
① MVStore 栈
// MVMap 属于 MVStore
public class MVMap<K, V> {
MVStore store;
}
// MVStore 管理多个 MVMap
public class MVStore {
ConcurrentHashMap<Integer, MVMap<?, ?>> maps;
FileStore fileStore; // 底层 I/O
MVMap<String, String> meta; // 元信息 map
}
// MVTable 持有 TransactionStore
public class MVTable extends TableBase {
TransactionStore store;
// 主键/索引数据落在若干 MVMap 上
}
核心关系:
MVMap.store→ 每个MVMap属于一个MVStoreMVStore.maps→ 一个MVStore管理多个MVMapMVStore.fileStore→MVStore持有一个mvstore.FileStore做底层 I/OMVStore.meta→ 特殊的MVMap<String,String>记录所有 map 的元信息MVTable.store→MVTable持有TransactionStore,后者包装MVStore
② PageStore 栈
// RegularTable 的索引持有 PageStore
public class PageDataIndex extends Index {
PageStore store;
}
public class PageStore {
FileStore file; // store.FileStore,与 mvstore.FileStore 不同
}
// Database 整库共享一个 PageStore 实例
public class Database {
PageStore pageStore;
}
核心关系:
RegularTable的索引(PageDataIndex、PageBtreeIndex)持有PageStorePageStore.file→PageStore持有一个store.FileStoreDatabase.pageStore→ 整库共享一个PageStore实例
平行对应关系
graph LR
subgraph L1["表层"]
A1["RegularTable"]
B1["MVTable"]
end
subgraph L2["事务层"]
B2["TransactionStore"]
end
subgraph L3["存储引擎层"]
A3["PageStore"]
B3["MVStore"]
end
subgraph L4["逻辑数据结构层"]
A4["store.Page 固定页 B-Tree"]
B4["MVMap + mvstore.Page"]
end
subgraph L5["文件 I/O 层"]
A5["store.FileStore"]
B5["mvstore.FileStore"]
end
A1 --> A3 --> A4 --> A5
B1 --> B2 --> B3 --> B4 --> B5
① 表层
RegularTablevsMVTable:均继承TableBase,对应传统表与 MVCC 表。
② 事务层
MVTable通过TransactionStore支持MVCC;RegularTable直接耦合在PageStore中,没有独立事务层。
③ 存储引擎层
PageStore和MVStore是两套核心引擎,分别服务各自的上层表。
④ 逻辑数据结构层
PageStore侧使用store.Page固定页B-Tree;MVStore侧使用MVMap(有序K-V树),内部节点为mvstore.Page。
⑤ 文件 I/O 层
store.FileStore和mvstore.FileStore同名不同类,各自只服务本栈。
核心差异总结
| 维度 | MVStore 栈 | PageStore 栈 |
|---|---|---|
| 默认启用 | ✔ 默认 | ❌ 需显式配置 |
| 事务支持 | MVCC 多版本并发 |
默认传统锁机制(也可开启 MVCC) |
| 中间层 | MVMap(多 K-V 树容器) |
无,直接用固定页 B-Tree |
| 文件 I/O | mvstore.FileStore |
store.FileStore |
| 表实现 | MVTable |
RegularTable |
🎈 最关键的区别:MVMap 是 MVStore 独有的中间层。MVStore 是“多个 MVMap 的容器”,每张 MVTable 的数据/索引就是若干 MVMap;PageStore 侧没有这层,直接用固定页 B-Tree 索引。
🎈 RegularTable 有完整的 MVCC 事务语义,包括读己之写、隐藏其他事务未提交数据、写写冲突检测、提交与回滚;只是底层仍受 PageStore 全局同步与内存 undoLog 的限制,不如 MVTable 的 MVStore 原生 MVCC。
总结
- PageStore(传统) 栈:
RegularTable→PageStore→store.FileStore - MVStore 栈实现:
MVTable→(TransactionStore)→MVStore→mvstore.FileStore MVMap是MVStore独有的中间层,理解这点就能区分两套引擎的核心设计差异FileStore同名不同类,各自只服务本栈,是最底层的文件读写封装