4

周刊(第12期):Page oriented类存储引擎里可能同时存在多种结构

 2 years ago
source link: https://www.codedump.info/post/20220410-weekly-12/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

引言:本期聊一聊Page oriented类存储引擎内的数据结构组织。在满足“向磁盘读写的基本单位是物理页面”这个大前提下,这类存储引擎的可能同时存在多种结构。


page oriented类存储引擎里可能同时存在多种树形结构

存储引擎的分类

目前接触到的存储引擎,以向磁盘读写方式来分类的话,大体可以分为两类:

  • LSM-Tree结构。
  • Page oriented类。

LSM-Tree是“Log-Structured Merge-Tree”的简称,这类存储引擎写入一条数据的流程大体如下:

  • 向内存以及WAL日志中写入完成,即可认为写入成功。
  • 内存中的数据写满之后,将落盘到所谓的sstable中。
  • sstable分为多层,随着写入进行,不同层次的sstable数据将进行合并。

(图片引用自LSM树详解 - 知乎)

从上面简单的写入LSM的流程可以看到:无论是写入内存还是磁盘,这类存储引擎在写入新数据时(不是合并sstable流程),磁盘操作的单位是一条记录。而一条记录的长度,是不定长的。

与LSM-Tree类的结构不同的是,Page oriented类的存储引擎,向磁盘发起读写操作的基本单位是页面(page),一个页面通常的大小是2的次方,最小一般是1024字节,比如sqlite的存储,其页面大小为4K(可以修改编译选项配置页面大小)。

以一个物理页面为读写磁盘的基本单位,这也是这一类存储引擎之所以被称为”Page oriented类存储引擎“的原因。本文重点是介绍Page oriented类存储引擎的结构。

Page oriented存储引擎的结构

还是以之前介绍过的sqlite的架构图来开头:

这个架构由下往上依次是:

  • 系统层:提供不同系统级API的封装,比如文件读写、加解锁操作等。
  • 物理页面管理层:提供物理页面读写、缓存等功能。
  • 树形结构的实现:根据具体的树形算法,组织物理页面之间的逻辑关系(比如父子页面、兄弟页面),以及单个物理页面之内的数据的组织。

这里的重点是页面管理层和树形结构的实现这两部分:

  • 物理页面管理相当于是磁盘文件的”原材料供应商“,负责对它的客户也就是各种不同结构的实现提供物理页面这一”原材料“的读写、缓存管理,而它对这些材料被客户拿去做成了什么,一无所知。
  • 树形结构的实现,从页面管理器拿到了”物理页面“这个原材料之后,可以按照自己的算法和数据结构任意塑造成任何合理的结构。

可以看到,Page oriented存储引擎,在满足“向磁盘读写的基本单位是物理页面”这个大前提下,这类存储引擎的可能同时存在多种结构:可能只有B-Tree,也可能只有B+Tree。还有另一种情况是:这类存储引擎内部同时存在多种结构。

以sqlite为例,内部其实就存在两种结构:

  • 存储索引的index tree:结构为B-Tree,键为表索引,值为这一行数据的rowid,其中rowid为隐藏列,创建数据表时自动生成,这一列是自增整数。
  • 存储数据的table tree:结构为B+Tree,键为rowid,值为一行数据。

这两类存储引擎,由于同属于“Page oriented类存储引擎”,因此可以共用同一个物理页面管理器。

下面,以sqlite中的一个表为例来解释上面这个流程。

首先,创建一个表以及索引:

// 创建数据库COMPANY
CREATE TABLE COMPANY(
ID INT NOT NULL,
NAME TEXT NOT NULL,
AGE INT NOT NULL,
// 创建索引
CREATE INDEX id_index ON COMPANY (id);

上面这个建表以及创建索引之后,对应的在这个数据文件中就有了两个树形结构:

  • 存储COMPANY表数据的table-tree。
  • 存储索引idrowid关系的index-tree。

如果向这个表插入数据,比如:

INSERT INTO COMPANY (ID,NAME,AGE) VALUES (1, 'Paul', 32 );

那么,这个插入操作背后实际对应了向这两棵树的插入操作:

  • 首先,将这一行数据插入到table-tree中,同时得到rowid以及插入时候的id
  • 再将第一步得到的rowid以及id插入到index-tree中。

如果使用id_index索引来查询COMPANY表,比如:

select * from COMPANY where id = 1;

这个查询操作也实际上经过了上面这两个表:

  • 首先,通过存储id_indexrowid关系的index-tree,找到id=1对应的rowid
  • 然后,再根据第一步得到的rowid到table-tree中查询到这一行数据。
  • 存储引擎,按照对磁盘读写方式的不同,大体可以分为以下两类:

    • LSM-Tree:写磁盘的基本单位是一条记录,而一条记录大小是不定长的。
    • Page oriented:读写磁盘的基本单位是页面,页面大小为2的次方。
  • “Page oriented”类存储引擎的核心模块是页面管理器和树形结构的实现,前者提供物理页面这一“原材料”的读写操作,对页面内部的结构一无所知;后者组织管理物理页面间的逻辑关系,以及物理页面内部的数据。
  • 在满足“读写磁盘的基本单位是页面”的大前提下,“Page oriented”类存储引擎可以使用各种树形结构,还可能在同一个存储引擎中同时存在多种树形结构。
  • sqlite的实现,内部存在两种不同的树形结构:使用B-Tree来管理索引数据,以B+Tree来管理表数据。这是因为:

    • 索引数据的值只有rowid这样的整型数据,所以单个物理页面内能存储更多的索引数据,适合使用B-Tree这样“高而瘦”的结构来管理这类单条数据很小的数据。
    • 而B+Tree的树形结构是“矮而胖”的结构,更适合存储管理多种不定长的数据。

Git的第一版

2005年4月6日,Git发布了第一版。Git无疑是最伟大的开源软件之一,它的出现极大改变了开源软件的协作、开发方式。

根据这里的“史料”( Git十歲了!Git之父Linus Torvalds說古,大談Git開發秘辛 | iThome )记载:Linus最初只花了10天就写出了第一版可以跑的Git了。

使用Rust编写gRPC服务的初学者指南

最近在使用Rust编写gRPC服务,这篇教程讲解了这部分内容,包括一应一答模式、单向stream模式、双向stream模式都有对应的代码例子,见:Rust gRPC: A beginners guide to gRPC with Rust

在大公司工作的吐槽

一位在美国工作的工程师写的国外大公司(文中是亚马逊)晋升的一些槽点,看起来和国内大公司也差不多,见:关于升职 - Yang Letu - Medium

另外文中还推荐了一个推特上的吐槽:

Noah Kantrowitz on Twitter: “FAANG promo committees are killing Kubernetes: A Short Thread 🧵” / Twitter

《关于威尔史密斯打人,一位台湾老师的社会课引导思考》

关于威尔史密斯打人,一位台湾老师的社会课引导思考,见:https://www.facebook.com/hhsleo/posts/5543635368999794

(上面的文章可能需要FB权限才能打开,也可以看这篇微信公众号的转发:关于威尔史密斯打人,一位台湾老师的社会课引导思考

“我告訴學生,我今天扮演的角色,就像是政治人物或媒體,我蓄意餵養你片面的、我想要你知道的資訊,而有超過7成的人,在這個過程中,被我操弄了,你因為我每次餵養的資訊不同,而產生立場反覆的狀況!明明政治人物應該考慮的是公益,媒體應該報導的是真相,但我若故意要操弄輿論,我只要給你我要你知道的訊息就好,對我不利的,我一概不提。慢慢的,我就可以透過這種愚弄的手法,讓民眾變成對我死忠而深信不疑的禁臠而不自知,我要你膜拜你就膜拜,我要你打砸殺你就打砸殺,我要你剷除異己你就剷除異己,我要你上刀山下油鍋,你還會爭先恐後想要身先士卒。而這樣的現象,正在世界各地上演”


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK