实现一个简单的Database1(译文)

SQLite Database System Design and Implementation SQLite数据库系统设计与实现 英文原版PDF,非常好的SQLite源码学习文档 立即下载

“What I cannot create, I do not understand.” – Richard Feynman

I’m building a clone of sqlite from scratch in C in order to understand, and I’m going to document my process as I go.

译注:cstsck在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的,实现教程原文是英文,共有13篇,这里翻译过来以飨读者。原文标题:Let’s Build a Simple Database,本文是第一篇

Part 1 介绍&设置REPL

作为一名开发人员,在工作中我每天都使用关系型数据库。但是对我来说,它们是一个黑盒。我有一些问题:

  • 数据存储使用什么格式? (在内存与磁盘中)
  • 数据什么时候从内存转移到磁盘?
  • 为什么每张表只能有一个主键?
  • 事务回滚是怎么工作的?
  • 索引是什么格式的?
  • 全表扫描时什么时候发生,如何发生的?
  • 预处理语句(prepared statement)是使用什么格式存储的?

换句话说,数据库是怎么工作的?

为了弄清楚这些,我从头写了一个数据库。它是模仿sqlite实现的,因为sqlite设计小巧,并且相比于MySQL和PostgreSQL,它的功能相对要少很多,所以我希望能更容易的理解它。在实现上,整个数据库都存储在一个数据文件中。

Sqlite

在sqlite的网站上,有很多sqlite的内部文档(https://www.sqlite.org/arch.html)。另外我还拷贝了文档(SQLite Database System: Design and Implementation.)的一个副本(https://play.google.com/store/books/details?id=9Z6IQQnX1JEC)。

image-20220919090434505
sqlite architecture

(https://www.sqlite.org/zipvfs/doc/trunk/www/howitworks.wiki)

一个查询通过组件链来获取数据或者修改数据。前端如下组件:

  • 分词器(tokenizer)
  • 解析器(parser)
  • 代码生成器(code generator)

前端的输入是SQL语句。输出则是sqlite的虚拟机字节码(sqlite virtual machine bytecode),本质上是一个可以在数据库运行的编译程序。

译注:数据库实现查询优化模型分为传统的火山模型(Volcano model)与Code gen模型,本文作者实现的是code gen模型。

后端包括如下组件:

  • 虚拟机(virtual machine)
  • B-tree
  • 页管理(pager)
  • 系统接口(os interface)

virtual machine

虚拟机将前端生成的字节码作为指令。它接下来可以在一个或更多的表、索引上执行操作,表以及索引都是存储在叫B-tree的数据结构中。VM 本质上是字节码指令类型的一个大开关语句(a big switch statement on the type of bytecode instruction)。

B-tree

每个B-tree许多节点。每个节点是一个page的长度。B-tree可以通过执行命令到pager,从磁盘获取一个page或者保存回page到磁盘。

pager

pager接收命令来读取或者写入数据的pages。它是负责来读、写数据库文件的适当偏移位置。也负责保持当前访问的pages在内存中,并且决定何时这些pages需要写回磁盘。

os interface

系统接口与sqlite根据不同操作系统平台来编译不同,在这个系列教程中,我不准备去支持多平台适配。

千里之行始于足下,所以我们从一些简单的事开始:REPL

实现简单的REPL

译注:REPL,Read - Execute - Print - Loop,即读取 - 执行 - 打印输出 - 循环,这个过程。有时候翻译成交互式解释器
当你执行命令行命令时,sqlite开始读取-执行-打印循环:

~ sqlite3
SQLite version 3.16.0 2016-11-04 19:09:39
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> create table users (id int, username varchar(255), email varchar(255));
sqlite> .tables
users
sqlite> .exit
~

为了实现这样的效果,我们的主程序需要有一个无限循环来打印这个提示,获取一行输入,然后处理这行输入:

int main(int argc, char* argv[]) {
  InputBuffer* input_buffer = new_input_buffer();
  while (true) {
    print_prompt();
    read_input(input_buffer);

    if (strcmp(input_buffer->buffer, ".exit") == 0) {
      close_input_buffer(input_buffer);
      exit(EXIT_SUCCESS);
    } else {
      printf("Unrecognized command '%s'.\n", input_buffer->buffer);
    }
  }
}

我们定义一个InputBuffer来作为一个封装,封装围绕在我们需要存储的、与getline()函数交互的状态(稍后将对此进行详细介绍)

typedef struct {
  char* buffer;
  size_t buffer_length;
  ssize_t input_length;
} InputBuffer;

InputBuffer* new_input_buffer() {
  InputBuffer* input_buffer = (InputBuffer*)malloc(sizeof(InputBuffer));
  input_buffer->buffer = NULL;
  input_buffer->buffer_length = 0;
  input_buffer->input_length = 0;

  return input_buffer;
}

接下来, print_prompt()函数为用户打印出提示。在做这个之前需要读取每一行输入。

void print_prompt() { printf("db > "); }

读取命令行输入,需要使用getline()函数:

ssize_t getline(char **lineptr, size_t *n, FILE *stream);

(以下为getline的函数释义)

lineptr:

一个指针指向我们在buffer中包含的,从命令行读取的命令的变量。如果设置为NULL,它由getline()函数分配内存。并且后续由用户来释放,即使命令行的命令执行失败也能保证会被释放已分配的内存。

n:

一个指针变量,指向已经分配内存的buffer的大小(size)。

stream:

读取的输入流,这里是从标准输入读取的。

return value(返回值,ssize_t类型):

读取的字节数量,可能会比buffer的size小。

我们告诉getline()函数保存读取的命令行到 input_buffer->buffer,存储buffer的size到 input_buffer->buffer_length,保存返回值到 input_buffer->input_length。

buffer 在初始时是null,所以getline()函数分配足够的内存来存输入的命令行数据然后让buffer来指向这些数据。

void read_input(InputBuffer* input_buffer) {
  ssize_t bytes_read =
      getline(&(input_buffer->buffer), &(input_buffer->buffer_length), stdin);

  if (bytes_read <= 0) {
    printf("Error reading input\n");
    exit(EXIT_FAILURE);
  }

  // Ignore trailing newline
  input_buffer->input_length = bytes_read - 1;
  input_buffer->buffer[bytes_read - 1] = 0;
}

现在就可以定义一个函数来释放已分配给InputBuffer*实例和buffer中元素各自的数据结构的内存了(在read_input()函数中,调用getline()函数为 input_buffer->buffer 分配内存)。

void close_input_buffer(InputBuffer* input_buffer) {
    free(input_buffer->buffer);
    free(input_buffer);
}

在最后,我们解析并执行命令。现在这只是一个认可的命令:.exit,一个终止程序的命令。除此之外的命令,我们打印一个报错信息然后继续程序的循环。

if (strcmp(input_buffer->buffer, ".exit") == 0) {
  close_input_buffer(input_buffer);
  exit(EXIT_SUCCESS);
} else {
  printf("Unrecognized command '%s'.\n", input_buffer->buffer);
}

让我们来试试吧!

~ ./db
db > .tables
Unrecognized command '.tables'.
db > .exit
~

好了,我们得到了一个可以工作的REPL。在下一部分,我们将开始开发我们的命令语言。同时,下面是是这部分的全部程序代码:

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
  char* buffer;
  size_t buffer_length;
  ssize_t input_length;
} InputBuffer;

InputBuffer* new_input_buffer() {
  InputBuffer* input_buffer = malloc(sizeof(InputBuffer));
  input_buffer->buffer = NULL;
  input_buffer->buffer_length = 0;
  input_buffer->input_length = 0;

  return input_buffer;
}

void print_prompt() { printf("db > "); }

void read_input(InputBuffer* input_buffer) {
  ssize_t bytes_read =
      getline(&(input_buffer->buffer), &(input_buffer->buffer_length), stdin);

  if (bytes_read <= 0) {
    printf("Error reading input\n");
    exit(EXIT_FAILURE);
  }

  // Ignore trailing newline
  input_buffer->input_length = bytes_read - 1;
  input_buffer->buffer[bytes_read - 1] = 0;
}

void close_input_buffer(InputBuffer* input_buffer) {
    free(input_buffer->buffer);
    free(input_buffer);
}

int main(int argc, char* argv[]) {
  InputBuffer* input_buffer = new_input_buffer();
  while (true) {
    print_prompt();
    read_input(input_buffer);

    if (strcmp(input_buffer->buffer, ".exit") == 0) {
      close_input_buffer(input_buffer);
      exit(EXIT_SUCCESS);
    } else {
      printf("Unrecognized command '%s'.\n", input_buffer->buffer);
    }
  }
}

Enjoy GreatSQL 😃

关于 GreatSQL

GreatSQL是由万里数据库维护的MySQL分支,专注于提升MGR可靠性及性能,支持InnoDB并行查询特性,是适用于金融级应用的MySQL分支版本。

相关链接: GreatSQL社区 Gitee GitHub Bilibili

GreatSQL社区:

欢迎来GreatSQL社区发帖提问
https://greatsql.cn/

GreatSQL社区

数据库系统实现 英文第二版 [Database System Implementation] 这是数据库系统实现的第二版,好像网上还找不到第二版的。 立即下载

相关推荐

KittenDB:与SQLite类似,但与NoSQL类似。 NoSQLite

KittenDB:与SQLite类似,但与NoSQL类似。 NoSQLite

SQLite 轻量级的嵌入式关系型数据库的替代软件

6. **TiDB**、**Kingbase**、**AntDB**、**TuGraph**、**OceanBase**、**GBASE**、**MatrixOne**、**KaiwuDB**、**PolarDB**、**GoldenDB**、**YashanDB** 等,这些都是国内知名的数据库产品,它们在某些方面可能作为SQLite的替代品,尤其是在需要更高性能或特定功能时。4. **DbGate**:一个跨平台的数据库管理工具,支持多种数据库系统,包括SQLite。

地球空间 1867

基于Docker的D435i相机与IMU联合标定实战指南

本文详细介绍了基于Docker的D435i相机与IMU联合标定实战指南,涵盖kalibr镜像构建、IMU标定全流程、双目相机标定技巧及联合标定结果验证。通过Docker容器化部署,简化了复杂依赖环境,提升标定效率与精度,适用于机器人视觉与SLAM应用。

weixin_30872337的博客 239

【Android】Room —— SQLite的替代品

SQLite怎么就被Room给替代了?

宾有为的博客 3484

保姆级教程:用Arduino+MPU6050自制寻北仪(附MEMS陀螺精度测试报告)

本文提供了一份详细的Arduino与MPU6050自制寻北仪的保姆级教程,包括硬件选型、卡尔曼滤波算法优化、地磁场补偿及真北计算等关键技术。通过实测数据展示了MEMS陀螺仪在民用领域的高精度性能,帮助读者低成本实现1°以内的寻北精度。

weixin_42522964的博客 364

从平衡车到寻迹小车:IMU963RA九轴陀螺仪实战避坑与四元数融合指南

本文详细解析了IMU963RA九轴陀螺仪在机器人姿态控制中的应用,重点探讨了从六轴升级到九轴系统的技术路径及四元数融合算法。通过实战案例,揭示了如何利用地磁计补偿陀螺积分漂移,并提供硬件集成、传感器校准及算法实现的完整解决方案,有效将8小时漂移控制在2度以内。

weixin_27219587的博客 254

好用的单机数据库有哪些推荐?

问题背景:要求:(1)要比较好的支持XP和 win7(2)具有一些基本的数据库功能,比如外键,视图等(3)易于安装,维护起来也简单的在 Windows XP 和 Windows 7 环境下,选择一款适合的单机数据库,需要兼顾系统兼容性、功能完善性以及易用性。以下是几款符合要求的数据库推荐,每款数据库的特点和适用场景也有所不同。根据你的需求(支持 XP 和 Win7、功能齐全、易于安装维护),以下是具体推荐:如果你对数据库的资源占用敏感(如运行在老旧硬件上),建议选择 SQLite 或 Firebird;如果

小宝哥Code的专栏 3949

测试人必看!正交表与Allpairs工具对比:如何选择最适合你的组合测试方案?

本文深入对比了正交表、PICT与Allpairs三种主流组合测试方案,帮助测试人员根据项目场景做出最佳选择。文章分析了各自的核心原理、优劣势及适用场景,特别强调了在涉及复杂约束时PICT工具的强大功能,并提供了从原理学习到CI/CD集成的实战选型指南。

weixin_29224309的博客 267

一个比 SQLite 还好用的数据库,诞生了!

这是「进击的Coder」的第 895篇技术分享来源:GitHubDaily“ 阅读本文大概需要 7 分钟。 ”众所周知,很多小微型应用程序都需要一些数据处理和计算能力,但如果集成一个数据库就显得太沉重了,正因如此,小巧轻量的 SQLite 才会被广泛应用。不过,SQLite 也有些不方便的地方。SQLite 对外部数据文件及其它数据源的支持力度比较弱又很繁琐;它本身没有存储过程,需要用主程序配合...

静觅 722

3、图像中文字信息提取技术全解析

本文全面解析了图像中文字信息提取(Text Information Extraction,TIE)的技术原理、挑战及应用领域。详细介绍了图像中三种主要文字形式:场景文字、字幕文字和文档文字的特点及提取难点。系统阐述了TIE技术的主要流程步骤,包括文字检测、定位、提取与增强、识别等关键阶段,并对主流技术方法进行了分类比较,包括基于连通组件、边缘、纹理等方法的优缺点分析。文章还介绍了多种其他提取技术,如二值化方法、混合方法、基于梯度的方法等,并提出了一种基于立体帧的新型场景文字提取方法,通过实验验证了该方法在召

ll5678的博客 119

华为OD面试八股真题 - 操作系统相关 - 2

华为OD技术面,操作系统相关八股真题提供详细解答,这篇博客主要涉及于进程和线程相关知识点

qq_45776114的博客 967

实现一个简单的Database5(译文)

GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。 GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。 前文回顾 实现一个简单的Database1(译文) 实现一个简单的Database2(译文) 实现一个简单的Database3(译文) 实现一个简单的Database4(译文) 译注:cstsck在github维护了一个简单的、类似SQLite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第五篇,主要是实现数据持久化 Part

GreatSQL2021的博客 460

MATLAB代码实现了一个柱塞泵配流盘过流面积的计算和可视化功能

该MATLAB代码实现了一个柱塞泵配流盘过流面积的计算和可视化功能。输出曲线可直接用于验证设计的合理性(如是否存在过流面积突变或不足)。的不同范围,选择对应的几何模型计算过流面积。通过分段逻辑计算柱塞腔在不同旋转角度(过流面积在特定条件下会被限制(如。),确保结果符合物理实际。条件分支,根据旋转角度。

huanghm88的专栏 582

实现一个简单的Database4(译文)

cstsck在github维护了一个简单的、类似SQLite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第四篇,主要是使用rspec对目前实现的功能进行测试并解决测试出现BUG 译注:cstsck在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第四篇,主要是使用r

GreatSQL2021的博客 300

专栏目录:Linux 内存管理子系统的进化——从 MM 到 HMM

专栏梳理了Linux内存管理子系统从经典MM到异构内存管理(HMM)的演进历程。内容分为六大部分:首先介绍经典MM基础,包括虚拟地址空间、页表、VMA等核心机制;其次分析MM通用框架如页表遍历、NUMA迁移等;然后探讨设备内存引入的关键技术,如ZONE_DEVICE和dev_pagemap;接着深入解析HMM核心机制及其与DMA的集成;随后通过多个实际驱动案例展示HMM应用;最后讨论大页支持等进阶主题。专栏采用"经典机制→问题→扩展方案"的三段式结构,帮助开发者理解Linux如何通过HMM实现设备内存管理。

图形/计算GPU的驱动与虚拟化技术分享园地 875

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

实现一个简单的Database2(译文)

前文回顾:实现一个简单的Database1(译文) 译注:cstsck在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第二篇,主要是实现数据库的前端组件,编译器与虚拟机部分功能 Part 2 世界上最简单的SQL编译器与虚拟机 我们正在实现一个sqlite的克隆版本。sqlite的前端是SQL编译器,编译器用来解析字符串并输出一个内部的表示,叫做字节码。 这些字节码被传到虚拟机(virtual machine),在虚拟机中,字节码将被

GreatSQL2021的博客 486

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

实现一个简单的Database3(译文)

前文回顾 实现一个简单的Database1(译文) 实现一个简单的Database2(译文) 实现一个简单的Database3(译文) 译注:cstsck在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第三篇,主要是实现数据库的实现内存中的数据结构并存储数据 Part 3 在内存中,只追加的单表数据库 我们从一个小型的,有许多限制的数据库开始。现在数据库将: 支持两个操作:插入一行并打印所有行 数据驻留在内存中(没有持久化到磁盘

GreatSQL2021的博客 141

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

实现一个简单的Database12(译文)

GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。 GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。 作者: 花家舍 文章来源:GreatSQL社区原创 前文回顾 实现一个简单的Database系列 译注:cstack在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第十一篇,主要是实现递归搜索B-Tree Part 11 递归搜索B-Tree 上次我们在插入第15行数据报错的时

GreatSQL2021的博客 136

实现一个简单的Database10(译文)

GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。 GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。 作者: 花家舍 文章来源:GreatSQL社区原创 前文回顾 实现一个简单的Database系列 译注:cstack在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第十篇,主要是实现B-tree中叶子节点分裂 Part 10 叶子节点分裂 我们 B-Tree 只有一个节点,这看

GreatSQL2021的博客 626

实现一个简单的Database9(译文)

cstack在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第九篇,主要是实现B-tree的二叉搜索并解决主键重复问题

GreatSQL2021的博客 124

实现一个简单Database8(译文)

前文回顾 实现一个简单的Database系列 译注:cstack在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本文是第八篇,主要是对B-tree的叶子节点格式的实现 Part 8 B-Tree叶子节点格式 我们准备把表的格式从非排序的数组格式行(rows)改成B-Tree。这是一个相当大变化,需要多个篇幅才能实现。在本文结束时,我们将定义叶子节点的布局,支持插入键值对儿到单节点的B-Tree。但是首先,来回顾一下把数据结构(从数组a

GreatSQL2021的博客 171

实现一个简单Database6

GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。 GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。 前文回顾 实现一个简单的Database1(译文) 实现一个简单的Database2(译文) 实现一个简单的Database3(译文) 实现一个简单的Database4(译文) 实现一个简单的Database5(译文) 译注:cstsck在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的项目,可以很好的理解数据库是如何运行的。本

GreatSQL2021的博客 382

实现一个简单Database7

GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。 GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。 前文回顾 实现一个简单的Database1(译文) 实现一个简单的Database2(译文) 实现一个简单的Database3(译文) 实现一个简单的Database4(译文) 实现一个简单的Database5(译文) 实现一个简单的Database6(译文) 译注:cstsck在github维护了一个简单的、类似sqlite的数据库实现,通过这个简单的

GreatSQL2021的博客 342

php create database,CREATE DATABASE语句解释

本文为mariadb官方手册:CREATE DATABASE的译文。语法CREATE [OR REPLACE] {DATABASE | SCHEMA} [IF NOT EXISTS] db_name[create_specification] ...create_specification:[DEFAULT] CHARACTER SET [=] charset_name| [DEFAULT] CO...

weixin_36073392的博客 457

官宣:Cloudberry Database 正式加入 Apache 孵化器

Cloudberry 由 Greenplum Database 原始开发者创建,是一款领先且成熟的开源大规模并行处理(Massively Parallel Processing,MPP)数据库。它基于开源版的 Pivotal Greenplum Database® 衍生而来,但采用了更新的 PostgreSQL 内核,并具备更先进的企业级功能。Cloudberry 可以作为数据仓库使用,也可用于大规模分析和 AI/ML 工作负载。

m0_54979897的博客 1078

基于PaddleOCR的漫画自动翻译工具:从文字检测到译文渲染完整实现

OCR(光学字符识别)技术通过计算机视觉方法将图像中的文字转换为可编辑文本,其核心原理涉及文字区域检测和字符识别两个关键环节。在自然语言处理领域,机器翻译技术基于深度学习模型实现跨语言语义转换,为多语言内容理解提供技术支撑。漫画自动翻译结合OCR与机器翻译,解决了外语漫画阅读的语言障碍问题,通过文字检测、识别、翻译和译文渲染的完整流水线,实现翻页即译的流畅体验。该技术特别适用于日文、韩文漫画的本地化处理,其中PaddleOCR凭借对漫画版面的优化检测能力,配合离线翻译模型,可在无网络环境下完成高质量翻译任务

weixin_30432579的博客 430
上一篇: 活动 | 开源项目成熟度评测+入选开源社区成员 GreatSQL社区闪耀OSCAR 2022开源产业大会
下一篇: 实现一个简单的Database2(译文)
GreatSQL社区
博客等级 码龄5年 1210粉丝 390原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值