H2 Database MVStore 初始化
作用
MVStore(multi-version store):
H2 的默认存储子系统,支持多版本,持久化的、日志结构 的 键值存储。
功能
1 2 3 4 5 6 7 8 9 10 11 12
| Maps 每个 store 包含多个 mvMaps,可以通过 java.util.Map 接口访问 Versions 支持多版本 Transactions 支持事务(并发事务&两阶段提交) Concurrent Operations and Caching 支持并发读写 Log Structured Storage 日志结构化存储 支持文件存储和内存操作 支持可插拔
|
文件存储格式
1 2 3 4 5
| # 文件存储格式 [ file header 1 ] [ file header 2 ] [ chunk ] [ chunk ] ... [ chunk ]
# chunk 存储格式 [ header ] [ page ] [ page ] ... [ page ] [ footer ]
|
1 2 3 4 5
| 结构 File Header 文件头 Chunk Format 块 Page Format 页 Metadata Map 元数据
|
文件头有两个 file header 1 和 file header 2,内容完全相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| file header 1: H:2 代表H2数据库 block:2 最新 block 之一的开始 blockNumber(未必是最新) blockSize:1000 默认固定是 4096,对应 hex 1000 chunk:7 chunk id,通常与 version 相同 clean:1 是否是clean shutdown标识,官方文档上没有提到 created:1441235ef73 自 1970 年创建文件以来的毫秒数 format:1 目前固定1 version:7 chunk 的版本号 fletcher:3044e6cc fileHeader对应的 Fletcher-32 校验和 filer header 2: 内容和 header1 完全相同
|
chunks
每个版本对应一个 chunk,一个 chunk 可能对应多个 block。
每个 chunk 只包含在该版本中被修改的 page,以及这些 page 的所有父节点,递归到根 page。
如果 map 中的内容被更改、删除或添加,则会复制相应的 page,并在下一个 chunk 中存储修改后的 page。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| header chunk:1 chunk的ID block:2 chunk的第一个block的编号(乘以block大小可以得到文件中的位置) len:1 块的大小(以 block 数量为单位) map:6 最新map的ID,每次创建新map时递增 max:1c0 所有最大 page 大小的总和 next:3 下一个chunk的起始 block pages:2 chunk中 page 的数量 root:4000004f8c 元数据根页面(page)的位置 time:1fc chunk被写入的时间,从文件创建后的毫秒数开始计算 version:1 chunk的版本号 pages: 参见下面的 pages 结构 footer: chunk:1 block:2 version:1 fletcher:aed9a4f6
|
pages
存储 maps 的实际数据。
每个 map 都是一个 btree。
map 内容存储的是字节数组。
Page 对象表示了 btree 的一个节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 存储结构 length 页面的字节数 checksum 校验和,计算方法为 chunk id 异或 page 在 chunk 中的偏移量 offset 异或 page length。 mapId 该页面所属 map 的 ID len 该页面中 key 的数量 type (byte) 页面的类型。叶节点:0;内部节点:1 children 子节点位置 (long 类型数组;仅仅是内部节点) childCountsChild 页面的数量 keys 字节数组,数组存储了该节点的所有键,类型取决于数据类型 values 字节数组,(仅适用于叶子节点)存储了该节点的所有值,类型取决于数据类型
|
元数据映射,包含 用户映射的名称和位置以及 chunk metadata。
chunk 的最后一页包含该元数据映射的 root page。
该页面(直接或间接)指向所有其他 map 的根页面。
1 2 3 4 5 6 7 8 9
| chunk.xxx: 块 xxx 的元数据。这与 chunk header 的数据相同,加上活动页数和最大活动长度。 map.xxx: map xxx 的元数据。条目为:name、createVersion 和 type。 name.data: 名为 data 的 map & map id root.xxx: map xxx 的根位置。 setting.storeVersion:
|
流程
打开 mvStore 流程
MVStore#open 流程如下。启动阶段会根据 H2 的文件存储,创建对应的 fileChannel。创建 MVMap,然后根据文件存储的内容恢复 MVMap 内存结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| org.h2.mvstore.MVStore#open 打开 mvStore org.h2.mvstore.MVStore#<init> 创建mvStore org.h2.mvstore.SingleFileStore#<init> 1.创建文件存储 org.h2.mvstore.FileStore#<init> org.h2.mvstore.cache.CacheLongKeyLIRS#<init> 创建 page 缓存 org.h2.mvstore.SingleFileStore#open 2.打开文件存储 fileChannel org.h2.mvstore.SingleFileStore#open fileChannel#open 打开文件存储 org.h2.mvstore.SingleFileStore#lockFileChannel 文件存储 channel 加锁 org.h2.mvstore.FileStore#bind 3.文件存储绑定 mvStore org.h2.mvstore.MVMap#<init> 创建 mvMap,先创建一个 empty 叶子节点&原子设置 mvMap 的根 page 节点 org.h2.mvstore.MVMap#setRootPos 设置根节点 org.h2.mvstore.MVMap#readOrCreateRootPage 从存储中读取或创建 root page org.h2.mvstore.MVMap#setInitialRoot 原子设置根节点 org.h2.mvstore.MVMap#setWriteVersion 设置 write version org.h2.mvstore.FileStore#start 4.启动文件存储 读取文件存储,初始化 root page 设置到 mvMap
|
FileStore#start
启动文件存储时,会解析文件存储的结构,然后恢复内存结构.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 1.若存储为空 1.1.初始化公共头部属性 1.2.初始化存储 header 写出 header 到存储 2.若存储不为空 readStoreHeader 从文件存储中读取 & 恢复 header 1.解析文件头&定位 newest chunk 读取前两个block(两个header)的数据到 ByteBuffer 中 依次读取文件头 解析头部属性并做checksum 读取header里的version 校验两个header里的version相同 读取文件头里的 chunk id 和 block readChunkHeaderAndFooter 读取 chunk header 根据 blockNumber * blockSize 定位到 chunk header 的 position 进行 chunk header 读取 创建 chunk 对象校验 chunk 的起始 block 和文件头里 block 是否一致 读取 Chunk Footer chunk header 里记录了当前 chunk 的 block 数量,所以加上 blockNumber 偏移量能定位到 chunk footer 位置 读取 chunk footer 并解析属性创建 chunk 对象,校验 blockNumber chunk 读取成功则更新 newest chunk 2.读取最新的20个chunks setLastChunk(newest) 读取最新 chunk 里的 root page 设置到 mvMap 1.磁盘读取 root page chunk header 里存储了 root page 的位置,可以定位到 root page 先从 cache 读取 page,没有就从文件读取后放入 CacheLongKeyLIRS 缓存 2.原子设置 root page 到 mvMap
|
类