From 1b2da23ffb3160707aab7309436f7375989d8b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Mon, 25 Apr 2022 23:29:39 +0800 Subject: [PATCH] init doc --- README.md | 15 +++++++++ api/dbfile.md | 28 +++++++++++++++++ api/index.md | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ api/table.md | 41 +++++++++++++++++++++++++ api/types.md | 40 ++++++++++++++++++++++++ 5 files changed, 208 insertions(+) create mode 100644 api/dbfile.md create mode 100644 api/index.md create mode 100644 api/table.md create mode 100644 api/types.md diff --git a/README.md b/README.md index c7e3c2e..1283dfa 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,17 @@ # fumidb A simple and lightweight database written in C + +## Documentation +Most part of this README and the docs in `api` folder is written in Chinese. If you want to use it but have any problem in language, please contact me directly through the issues. + +## 支持的功能 +- [ ] 创建[数据库文件](/api/dbfile.md#数据库文件格式)和[表](/api/dbfile.md#新建表) +- [ ] [删除表](/api/dbfile.md#删除表) +- [ ] [插入表项](/api/table.md#表项) +- [ ] [删除表项](/api/table.md#表项) +- [ ] 对主键、外键进行[索引](/api/index.md) +- [ ] 对非主键(无[unique约束](/api/types.md#类型修饰符))进行[索引](/api/index.md) +- [ ] 修改表项 + +## Thanks +- [asciiflow](https://asciiflow.com/) \ No newline at end of file diff --git a/api/dbfile.md b/api/dbfile.md new file mode 100644 index 0000000..96e7adc --- /dev/null +++ b/api/dbfile.md @@ -0,0 +1,28 @@ +# 数据库文件格式 +由于文件内普遍以uint64作为指针,因此理论最大支持文件大小为`16384PB`,在现有条件下完全足够使用。 +## 文件头 +对于未被使用的对齐部分,其开头将有一个8字节指针指向下一块未被使用的对齐部分,没有则置0;接下来紧跟着uint16的数字,指示了本块未被使用的对齐部分的长度。因此,能加入链表的未被使用的对齐部分最短应为10字节,小于这个长度的未被使用部分将不再使用。 +``` +0 8 16 +┌───────────────────┬───────────────────┐ +│ ptr of unused blk │ ptr of next table │ +├───────────────────┴───────────────────┤ +│ first table head │ +├───────────────────────────────────────┤ +│ ...... ...... ...... │ +├───────────────────┬───────────────────┤ +│ ptr of next table │ second table head │ +├───────────────────┴───────────────────┤ +│ ...... ...... ...... │ +├───────────────────────────────────────┤ +│ some possible padding to fit 4096Byte │ +├───────────────────────────────────────┤ +│ data blocks ... │ +└───────────────────────────────────────┘ +``` +### 新建表 +在新建表时将计算表头大小,优先选取一块未被使用的足够大的对齐部分写入表头。当找不到时,在文件末尾附加表头(并留出新的对齐)。接下来将上一个表头开头的`下一个表头的指针`指向新表头的开头,然后建立相应数据结构,填充表头字段。 +### 修改表 +一旦创建数据表,将不支持修改。可以先删除表再重新创建,但这样数据将会丢失。 +### 删除表 +根据表头遍历所有表项,回收空间到未被使用的对齐部分,然后再回收表头,更新表头链表的指针,完成删除。 \ No newline at end of file diff --git a/api/index.md b/api/index.md new file mode 100644 index 0000000..2df4cbc --- /dev/null +++ b/api/index.md @@ -0,0 +1,84 @@ +# 索引格式 +## int8 +> 查找速度为O(1) + +由于总条目仅有256条,因此直接存储256个uint64的位置指针,指示该条目在文件的偏移。 + +> 下面每格1字节 +``` +┌─────────┬─────────┬─────────┬─────────┐ +│ ptr 000 │ ptr 001 │ ptr ... │ ptr 255 │ +└─────────┴─────────┴─────────┴─────────┘ +``` +## int16 +> 查找速度为 +> - 无该表项:O(1) +> - 有该表项:O(logn) + +由于总条目仅有65536条,因此使用位图索引+位图+顺序链表进行查找定位。 +### 位图与位图索引 +每一位代表一个槽位,为0表示当前为空,为1表示当前已有值,按顺序排列。 +> 下面每格1字节 +``` +┌────────┬────────┬────────┬────────┐ +│00100011│00000000│11001010│11000110│ +└────────┴────────┴────────┴────────┘ +``` +每256位(32字节)为一组,生成8位(1字节)位图索引,插在该组最前。该值指示在这256个槽位中有多少个已被填充。特别地,如果256个槽位均被填满,索引也为`0`,因此还需要额外判断其对应位图是全空还是全满。只要有一处不为0而位图索引为0,即可判定这256个槽位全满。 +> 下面每格1字节 +``` +┌────────┬────────┬────────┬────────┐ +│ 30 │ No.000 │ No.... │ No.255 │ +└────────┴────────┴────────┴────────┘ +``` +### 顺序链表 +根据位图和位图索引可以很方便地计算出当前值在顺序链表上的位置。顺序链表以256个uint64的位置指针为单位分配,当装满后分配一块新的空间,并将新链表开头的位置指针记录在旧链表开头。当没有下一个节点时,开头置0。特别地,当删除表项时,节点数有可能减少。此时并不归还多余节点所占空间,也不对节点头部指针做任何改动,而是将其保留以备后用。 +> 下面每格8字节 +``` +┌────────┬────────┬────────┬────────┐ +│next ptr│ ptr000 │ ptr... │ ptr255 │ +└────────┴────────┴────────┴────────┘ +``` + +## int32/float +使用B+树建立索引,每个节点大小为`4088`字节,可对齐到`4096`字节以方便`mmap`,最多可有`n=341`个扇出,`340`个值;最少则有`170`个值(根节点不遵守最少值规则)。 +> 下面每格4字节 +``` + 0 8 12 20 + ┌───────────────────┬─────────┬───────────────────┬─────────┐ + 0│ pointer 001 │ key 001 │ pointer 002 │ key 002 │ + ├───────────────────┼─────────┼───────────────────┼─────────┤ + 24│ pointer 003 │ key 003 │ pointer 004 │ key 004 │ + ├───────────────────┼─────────┼───────────────────┼─────────┤ + 48│ pointer 005 │ key 005 │ pointer 006 │ key 006 │ + ├───────────────────┼─────────┼───────────────────┼─────────┤ + xxx│ pointer xxx │ key xxx │ pointer xxx │ key xxx │ + ├───────────────────┼─────────┼───────────────────┼─────────┤ +4056│ pointer 339 │ key 339 │ pointer 340 │ key 340 │ + ├───────────────────┼─────────┴─────────┬─────────┴─────────┘ +4080│ pointer 341 │ unused field │ + └───────────────────┴───────────────────┘ + 4088 4096 +``` +## int64/double +使用B+树建立索引,每个节点大小为`4088`字节,可对齐到`4096`字节以方便`mmap`,最多可有`n=256`个扇出,`255`个值;最少则有`128`个值(根节点不遵守最少值规则)。 +> 下面每格8字节 +``` + 0 8 + ┌───────────────────┬───────────────────┐ + 0│ pointer 001 │ key 001 │ + ├───────────────────┼───────────────────┤ + 16│ pointer 002 │ key 002 │ + ├───────────────────┼───────────────────┤ + 32│ pointer 003 │ key 003 │ + ├───────────────────┼───────────────────┤ + xxx│ pointer xxx │ key xxx │ + ├───────────────────┼───────────────────┤ +4064│ pointer 255 │ key 255 │ + ├───────────────────┼───────────────────┤ +4080│ pointer 256 │ unused field │ + └───────────────────┴───────────────────┘ + 4088 4096 +``` +## string +先将其哈希为int64再按int64进行查找。冲突时根据string表项附带存储的[下一个哈希相同的数据项的指针(uint64)](/api/types.md#字符串)进行遍历。 diff --git a/api/table.md b/api/table.md new file mode 100644 index 0000000..0097abb --- /dev/null +++ b/api/table.md @@ -0,0 +1,41 @@ +# 数据表格式 +## 表头 +如下所示,其中行类型列表的No.1自动成为主键,强制应用`unique`类型修饰符;`data blocks`可以为任意数据,如索引,表项等。由于数据块均为定长,增加时直接添加或重用已删除区块,修改时直接覆盖,删除时直接在索引中移除该项,将块首地址附加到已删除块的链表即可。 +``` +┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐ +│ 0 - 7 │ 8 - 15 │ 16 -- 23 │ 24 -- 31 │ 32 -- 39 │ 40 -- 47 │ 48 -- 55 │ 56 -- 63 │ +├──────────┴──────────┼──────────┴──────────┴──────────┴──────────┴──────────┴──────────┤ +│ table name length │ name of the table ( variable length ) │ +├─────────────────────┼──────────┬──────────┬──────────┬──────────┬──────────┬──────────┤ +│ │ type of │ type of │ type of │ type of │ type of │ type of │ +│ table column length │ column │ column │ column │ column │ column │ column │ +│ │ No.1 │ No.2 │ No.3 │ No.4 │ No... │ No.N │ +├─────────────────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┤ +│ index pointer of pk ( this pointer will never be zero ) │ +├───────────────────────────────────────────────────────────────────────────────────────┤ +│ index pointer of column No.2 ( if it's zero, there is no index for this column ) │ +├───────────────────────────────────────────────────────────────────────────────────────┤ +│ index pointer of column No... ( if it's zero, there is no index for this column ) │ +├───────────────────────────────────────────────────────────────────────────────────────┤ +│ index pointer of column No.N ( if it's zero, there is no index for this column ) │ +├───────────────────────────────────────────────────────────────────────────────────────┤ +│ index pointer of first deleted block ( if it's zero, there is no deleted block ) │ +├───────────────────────────────────────────────────────────────────────────────────────┤ +│ index pointer of first foreign key ( if available ) │ +├───────────────────────────────────────────────────────────────────────────────────────┤ +│ index pointer of second foreign key ( if available ) │ +├───────────────────────────────────────────────────────────────────────────────────────┤ +│ data blocks ... │ +└───────────────────────────────────────────────────────────────────────────────────────┘ +``` +## 数据区块 +### 索引 +> 区块长度固定,但是不同索引类型有所不同 +详见[索引格式](/api/index.md)。 +### 表项 +> 区块长度固定,为`8+len(column1)+len(column2)+...+len(columnN)`字节 +为方便遍历,数据表项以uint64的指针开头,代表下一项的地址。接下来按照[数据类型](/api/types.md)中规定的存储格式依次附加第一项、第二项直到第N项的值。 +#### 表项的增加 +优先附加到上一个表项末尾。如无法实现,则从空区块选取一个,或附加到整个文件末尾。 +#### 表项的删除 +需要更新[未被使用的对齐部分](/api/dbfile.md#文件头) \ No newline at end of file diff --git a/api/types.md b/api/types.md new file mode 100644 index 0000000..7eba38d --- /dev/null +++ b/api/types.md @@ -0,0 +1,40 @@ +# 数据类型 +> 每种数据类型都必须指定大小(8位、16位、32位、1024字节等),不存在模糊的类型。 +> 所有数字均按照小端字节序存储。 + +## 数字 +> 在类型中并没有无符号数,实际使用时如需无符号数,可以进行强制类型转换。 +### 类型列表 +| 类型代号 | 类型 | 存储方式 | +| --- | --- | --- | +| 0 | int8 | 直接存储 | +| 1 | int16 | 直接存储 | +| 2 | int32 | 直接存储 | +| 3 | int64 | 直接存储 | +| 4 | float | 直接存储 | +| 5 | double | 直接存储 | + +## 字符串 +> 字符串为定长的带索引的字节数组,长度最大不超过uint32(65535字节)。 +### 类型列表 +| 类型代号 | 类型 | 存储方式 | +| --- | --- | --- | +| 6 | string | 下一个哈希相同的数据项的指针(uint64)+该长度的数据(字节数组) | + +## 二进制数据块 +> 二进制数据块为定长(不超过uint64)的不参与索引的字节数组。 +### 类型列表 +| 类型代号 | 类型 | 存储方式 | +| --- | --- | --- | +| 7 | binary | 直接存储数据(字节数组) | + +## 类型修饰符 +> 类型修饰符占据类型代号的高2位,通过或运算与基础代号结合 +| 类型代号 | 类型 | 说明 | +| --- | --- | --- | +| 0x00 | null | 无修饰:允许重复、可空、非外键 | +| 0x40 | unique | 不允许重复 | +| 0x80 | nonnull | 非空 | +| 0xc0 | foreignkey | 外键 | + +特别地,对于外键修饰符,将会在表头标明其外键连接到的表头指针。 \ No newline at end of file