数据库入门指南:数据库存储架构 | 青训营

117 阅读4分钟

典型架构

B+树存储架构

采用B+树文件组织方式,

  • 表空间 Table space:在 MySQL Innobase文件管理中,可以用一个文件存储所有表的数据,也可以为每一个表设置一个自己独立的表空间文件。

  • 段 Segment:表空间内按存放内容分段。创建一个B+树时至少会创建两个段,分别是内节点段和叶子节点段。内节点段中存放内节点所在块,叶子节点段存放叶子节点所在块。

  • 区 Extent:段内分区。当表里存储的数据非常多的时候,并不是按照页为单位来分配空间,而是按照区的单位来分配的。区内是连续的页,相当于连续分配,连续访问比随机访问IO性能更好,但可能增加内部碎片。

  • 页 Page:页存放B+树的一个节点。

    • 叶子节点:包含主键和逻辑记录,以及前后页指针(B+树的叶子节点组织成双向链表)
    • 非叶子节点:包含主键和孩子节点的指针

基于 B+树的存储需要较多的磁盘随机读写,

( Log-Structured)的新型存储架构的主要思想是将磁盘看作一个大的日志,每次都将新的数据添加到日志的最末端,以实现对磁盘的顺序写,提高写性能,但日志结构方法的随机读效率较低。

日志结构存储的核心理念是磁盘的顺序写速度要比随机写速度快得多,适合高密度写入,低密度查询的场景。

Bitcask存储架构

KVS键值存储系统的基本接口:

  • 读写
leveldb::Status s;
s = db->Get(leveldb::ReadOptions(), key, &value);
s = db->Put(leveldb::WriteOptions(), key, value);
s = db->Delete(leveldb::WriteOptions(), key);
  • 迭代器
leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
for (it->Seek(start);it->Valid() && it->key().ToString() < limit;it->Next()) {
  // 输出
}

Bitcask是基于哈希表的日志型结构健值对存储系统(Log-structured store),Bitcask包括:

  • 磁盘日志型数据文件:

    • 活跃数据文件:采用追加写的方式,append only。在任意时间点,只有一个文件是可写的,即活跃数据文件。
    • 老数据文件:只读不写,read only

    数据以日志形式存在,对数据库的更新以日志方式追加写入活跃数据文件中,记录value的最新值。当活跃数据文件大小增加到一定大小时,产生一个新的活跃数据文件,在新的活跃数据文件继续追加写,之前的活跃数据文件变为老数据文件。

    老数据文件合并操作(merge) :定期将所有老数据文件中的数据扫描一遍并生成新的老数据文件(不包括活跃数据文件,因为它还在不停写入),按照同一个key的多个操作以只保留最新一个的原则进行删除,消除冗余数据。

  • 内存哈希索引:

    数据的索引项按key和散列函数放入对应的桶中。

    索引项指向磁盘中数据位置,索引项中包括定位数据value的信息:

    • 文件编号
    • value在文件中的位置
    • value长度

读写操作:

  • 读:根据内存哈希表中索引项的文件编号和value位置定位value在磁盘中的位置,读取value长度个字节,得到需要的value值(一次磁盘随机读)。

  • 写:在活跃数据文件追加日志记录(一次磁盘顺序写),更新哈希表的索引项,指向新的日志记录位置,以后读到的就是新的数据。

    数据删除操作也不会删除旧的条目,而是追加日志将value设定为一个特殊的值以作标示。

优点和缺点:

  • 优点:只有顺序写,消除了随机写,写性能好

  • 缺点:

    • 理论上是随机读性能一般,但根据局部性,可以添加缓存优化。

    • 哈希索引不支持区间查找。

    • 由于索引哈希表在内存中,索引项的数量受内存容量限制。

    • 如果发生系统重启,则须要扫描磁盘中的数据重建哈希表,如果数据量非常大,过程非常耗时。

      可以对老数据文件生成一个hint file,这个文件中的数据结构与磁盘中的数据文件非常相似,不同的是它不存储具体的value值,而是存储value的位置信息,相当于对旧数据哈希表的物化。