Database Storage
File Storage
DBMS 会把数据库存为一个或多个文件,每个文件由一组 Page 组成。Page 是固定大小的块(block),Page 可以存放 tuple, meta-data, indexes 等,每个 page 都有唯一的 ID 标识。
Page Layout
Page Header
Page Header 用于存储 Page 元数据(meta-data),包括:页大小、事务信息、校验信息和压缩信息等。
Page Data 用于存储 Tuple 数据,主要有:Tuple-oriented 和 Log-structured 两种方式。
Tuple-oriented Storage
Tuple 可以看作表中的每一行数据,大多数 DBMS 采用 page_id + offset/slot 定位每一条 Tuple 记录。
Header 主要用于存放元数据(meta-data),主要包括:并发控制信息,记录 NULL 值的位图(Bit Map)。
Tuple 内每一列的类型,一般单独存放在另一 Tuple 中。
固定长度不利于删除和可变长度的 Tuple 存储,目前多采取 Slotted Page 方式。
Slotted Page 利用 Slot Array 记录每一个 Tuple 开始位置索引,每个 Tuple 的长度是可变的。Slot Array 的长度随着 Tuple 的数量增减。
Slot Array 自上而下扩展,Tuple 自底向上扩展。
当删除 Tuple 后,只需删除 Slot Array 记录的信息即可,可以在空闲时间或删除后立即重新排列 Tuple。
Log-structured Storage
对于 Log-structured 格式的存储,只需支持 PUT 和 DELETE 命令。对于数据库的任何操作都只采取追加日志的方法(append log),从不删除或修改以前存储的记录,这样可以减少复杂度和 IO 次数。
当通过 KEY 查找记录时,只需从最新的 Page 开始从后往前顺序查找,找到的第一条即可对应的结果。
为了提升速度,可以在内存中维护对应的索引结构,如:哈希表、跳表和树;
注意: 哈希表不支持范围查找,如:查找 ID 从 102 至 105 的记录
当 Log 一直增加时,占用的空间会越来越多,需要周期的压缩(compact)日志,通过将相同的日志项合并一起,减少空间的占用。
压缩需要锁保证并发安全,比较耗时。同时会造成写放大(write amplification),可以参考此论文。
Tuple-oriented 和 Log-structured
Tuple-oriented 方式新增(更新) Tuple 需要通过 Page Directory 找到有空闲插槽(slot)的页(page),从磁盘中加载页到内存中,把数据(更新后的数据)填充到从页中 Slot Array 找到的空闲空间。
Log-structured 所有操作只需追加到文件中即可。
mmap
为何数据库系统不推荐使用 mmap?无法控制 Page 何时换入换出内存
page size
较大的 page size 可以减少 syscall 的次数,减少从磁盘加载到内存的次数。
较小的 page size 有利于内存利用率,
MySQL 的 InnoDB 引擎 page size 默认为 16KB,可以通过 innodb page size 更改。
Large Value
大多数 DBMS 不允许存储超过 Page Size 的值,可以采取 Overflow Page 存取值。当值过大时,可以存储到多个 Overflow Page 中并链接起来。
此外可以采取 External File 存储值(BLOB 类型),此方式缺少事务和持久化保护。
System Catalog
Catalog 用于存储数据库内部信息,如:所有表的信息、运行配置和索引信息等。
select *
from information_schema.TABLES;