千家信息网

SQLite数据库的工作原理分析

发表于:2024-11-25 作者:千家信息网编辑
千家信息网最后更新 2024年11月25日,这篇文章主要为大家展示了"SQLite数据库的工作原理分析",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"SQLite数据库的工作原理分析"这篇文章吧。SQ
千家信息网最后更新 2024年11月25日SQLite数据库的工作原理分析

这篇文章主要为大家展示了"SQLite数据库的工作原理分析",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"SQLite数据库的工作原理分析"这篇文章吧。

SQLite 是一个小型数据库应用程序,用于数百万个软件和设备。SQLite 是由 D.Richard Hipp 于 2000 年 8 月发明的。SQLite 是一种高性能、轻量级的关系数据库。如果您愿意在编码级别学习数据库的内部结构,那么 SQLite 是最好的开源数据库,它具有高度可读的源代码和大量文档。阅读更高版本的 SQLite 变得有点困难,因为它包含许多新功能。为了理解数据库内部的基本实现,你应该对数据结构有很好的了解,一些关于计算理论的知识,以及操作系统是如何工作的。

在这里,我们将研究 SQLite 2.5.0 版本。您可以在GitHub上找到SQLite 后端的简单实现。 另外,我发现这个存储库 包含用于代码读取的 SQLite 2.5.0 实现。

什么是数据库?

将数据保存在平面文件中读取和存储数据的效率不高。数据库以适当的顺序组织数据,以便数据读取和写入速度更快。数据可以是结构化的、半结构化的或非结构化的。数据库主要用于存储结构化和半结构化数据。可以根据要存储的数据结构类型对数据库进行如下挖掘。

  1. 关系型数据库:常用的具有表结构的数据库类型。表可以与其他表有关系。SQL 语言用于操作此类数据库上的数据。

  2. 键值数据库:与关联的键一起存储的数据。可以使用给定的键检索数据。内存数据库通常是这种类型的数据库。

  3. 对象数据库:数据结构更像是一个对象而不是一个表。

  4. 图数据库:图数据库是节点和边的集合,主要用于数据挖掘和社交媒体应用程序。

SQLite 数据库架构

SQLite 数据库架构分为两个不同的部分,分别命名为核心和后端。核心部分包含接口、分词器、解析器、代码生成器和虚拟机,它们为数据库事务创建执行顺序。后端包含 B-tree、Pager 和 OS 接口来访问文件系统。Tokenizer、Parser 和代码生成器统称为编译器,它生成一组运行在虚拟机上的操作码。

从哪里开始?

要了解数据库的架构,您需要具备以下先决条件。

  1. 对数据结构和算法有很好的理解。尤其是 B 树、链表、Hashmaps 等数据结构。

  2. 对计算机体系结构有一定的了解。如何读写磁盘,分页和缓存如何工作。

  3. 有限自动机、上下文无关语法等理论计算机和一些正则表达式知识。

SQLite 架构

cdn-images-1.medium.com/max/1600/1*t5ygkzu6a28fO41ynZ8kgQ.jpeg">

VFS(虚拟文件系统)

Unix 和 Windows 上的文件访问彼此不同。VFS 提供通用 API 来访问文件,而无需考虑其运行的操作系统类型。该 API 包括打开、读取、写入和关闭文件的函数。以下是 VFS 中用于读取、写入数据到文件中的一些 API。

/* Create a connection to file to read write zFilename : file name id : file pointer pReadonly : read or write */int sqliteOsOpenReadWrite(const char *zFilename, OsFile *id,int *pReadonly);/* Acqure the lock to read file. This function should be called before caling to any file read function. Return 0 if successid : file pointer */int sqliteOsReadLock(OsFile *id);/* Get the write lock to write into a file. This function should called beforedoing any write operation into the file system.Return 0 if successid : file pointer*/int sqliteOsWriteLock(OsFile *id);/* Move to the given number of offest to read or write into the file*/int sqliteOsSeek(OsFile *id, int offset);/* Read amt bytes from the file with offset pointed by sqliteOsSeek*/int sqliteOsRead(OsFile *id, void *pBuf, int amt);/* Write amt bytes from the pBuf into the file*/int sqliteOsWrite(OsFile *id, const void *pBuf, int amt);

在这里,您可以使用 sqliteOpenReadWrite 函数开始使用文件。此函数为您提供一个指向文件的指针,该文件可用于读取或写入数据。接下来,应该在执行任何读写操作之前获取锁。如果它只是一个读操作,那么应该获取读锁。应该为读和写事务获取写锁。

然后可以通过将文件的偏移量提供给 sqliteOsSeek 函数来查找位置来完成读写。偏移量是从文件的起点到数据应该写入或读取的位置的字节数。

Pager

页是文件系统上数据库事务的最小单位。当数据库需要从文件中读取数据时,它会将它作为一个页面来请求。一旦页面被加载到数据库引擎中,如果它经常访问它的缓存,它就可以存储该页面。页数从一页开始编号。第一个页面称为根页面。页的大小是恒定的。

/*Open pager with the given file name*/int sqlitepager_open(Pager **ppPager,const char *zFilename,int nPage,int nEx);/*Get page specified by the page number*/int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage);/*Start to write data into a page specified in pData*/int sqlitepager_write(void *pData);/*Commit page changes into the file*/int sqlitepager_commit(Pager*);/*Close the connection to the file*/int sqlitepager_close(Pager *pPager);

Btree

Btree 是一种数据结构,用于根据数据的值将数据存储为树。BTree 最简单的形式是二叉树。数据库使用 Btree 数据结构来存储索引以提高数据库的性能。Btree 的每个节点都包含一个用于索引的键列表。可以为表中的每一列创建 Btree 索引。每个 Btree 都有根页面,这是任何 Btree 搜索的起点。

为了指向 Btree 上的一行,使用了称为"Cursor"的特殊指针。游标用于指向一个记录,该记录由页面 id 和偏移量指定,称为 idx。SQLite 将数据库模式存储在称为"master_table"的表上。master_table 总是存储在数据库的第一页上。

了解更多关于SQLite的B树的设计在这个文章。

/*Open file connection to a page file name specified by zFileName with nCache size cache*/int sqliteBtreeOpen(const char *zFilename, int mode, int nCache, Btree **ppBtree)/*Start transaction. This function should called before any btree modification operations*/int sqliteBtreeBeginTrans(Btree *pBt)/*Insert key pKey with nKey byte and value pData with nData byte put into the Btree*/int sqliteBtreeInsert(BtCursor *pCur, const void *pKey, int nKey,                       const void *pData, int nData)/*Write data into the file*/int sqliteBtreeCommit(Btree *pBt)/*Move cursor to the matching pKey with nKey bytes*/int sqliteBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes)

VDBE(虚拟数据库引擎)

VDBE 是运行一组操作的虚拟机,由代码生成器生成。包括插入、删除、更新、选择在内的所有 SQL 命令都转换成一组操作码,然后在虚拟机上运行。每个操作码包含三个名为 p1、p2 和 p3 的输入。您可以将此输入视为函数的输入。

下面我为以下 SQL 选择语句添加了示例执行操作码堆栈。PC 是程序计数器的指令 ID。 对我来说,SQLite 中最有趣的事情是我可以通过在 SQL 查询的开头附加"explain"关键字来查看给定 SQL 代码的一组 VBDE 操作码指令。

explain select * from foo;
个人电脑操作码P1P2P3评论
1列数10
将列数设置为 1
2列名00价值将列名设置为"值"
3打开03

打开光标并将其指向第三页,即 foo 表的根页(p3 不重要

4验证Cookies460
确保架构未更改
5倒带011
将光标移动到第一个条目
6柱子00
从 Btree 负载读取数据并将其放入堆栈
7柱子00

8ne110

从堆栈中弹出顶部的两个元素。如果它们不相等,则跳转到指令 P2。否则,继续执行下一条指令。

9打回来10

从堆栈中弹出 P1 值并将它们组成一个数组。这将是此 SQL 表达式的结果行

10下一个05

将光标移动到下一条记录,如果数据退出转到 P2 否则转到下一行

11关闭00
关闭光标

编译器

Tokenizer、Parser 和 Code Generator 统称为编译器,可生成在 VBDE 上运行的操作码集。Tokenizer 通过扫描 SQL 代码生成一组令牌。然后,它验证语法并生成解析树。Lemon 解析器用于通过预定义的上下文无关语法来解析给定的 SQL 代码。代码生成器将这个解析树转换成一个用 SQLite 操作码编写的小程序。

以上是"SQLite数据库的工作原理分析"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

0