数据库是构建软件系统的重要组成部分,用于有效地存储和读取数据。在这里,我们将使用早期版本的SQLite讨论数据库实现的一些体系结构细节。
SQLite是一个小型数据库应用程序,用于数百万个软件和设备。SQLite是由D.Richard Hipp于2000年8月发明的.SQLite是一个高性能,轻量级的关系数据库。如果您愿意在编码级别学习数据库的内部,那么SQLite是最好的开源数据库,具有高度可读的源代码和大量文档。阅读更高版本的SQLite变得有点困难,因为它包含许多新功能。为了理解数据库内部的基本实现,您应该对数据结构,计算理论以及操作系统的工作原理有一定的了解。
在这里,我们将研究SQLite 2.5.0版本。您可以在GitHub上找到 SQLite后端的简单实现。
将数据保存在平面文件中并不能有效地访问和保存数据。数据库按正确顺序组织数据,以便数据读取和写入速度更快。数据可以是结构化的,半结构化的或非结构化的。数据库主要用于存储结构化和半结构化数据。可以基于用于实现软件的数据结构的类型,如下潜水数据库。
SQLite数据库体系结构分为两个不同的部分,分别命名为核心和后端。核心部分包含接口,令牌生成器,分析器,代码生成器和虚拟机,它们为数据库事务创建执行顺序。后端包含访问文件系统的B树,寻呼机和OS接口。Tokenizer,Parser和代码生成器共同命名为编译器,它生成一组在虚拟机上运行的操作码。
要了解数据库的体系结构,您需要具备以下先决条件。
Unix和Windows上的文件访问权限彼此不同。VFS提供了访问文件的通用API,而不考虑其运行的操作系统的类型。此API包括打开,读取,写入和关闭文件的功能。以下是VFS中用于读取,将数据写入文件的一些API。
/ *创建连接到文件以读取写入zFilename:文件名id:文件指针pReadonly:读取或写入* / int sqliteOsOpenReadWrite(const char * zFilename,OsFile * id,int * pReadonly);
/ *获取锁定以读取文件。如果成功则返回0如果失败则返回0:文件指针* / int sqliteOsReadLock(OsFile * id);
/ *获取写锁定以写入文件。如果成功则返回0如果失败则返回0:文件指针* / int sqliteOsWriteLock(OsFile * id);
/ *移动到给定的offest 数量来读取或写入文件* / int sqliteOsSeek(OsFile * id,int offset);
/ *从sqliteOsSeek * / int sqliteOsRead(OsFile * id,void * pBuf,int amt)指向的偏移量读取文件中的amt字节 ;
/ *将ptf中的amt字节写入文件* / int sqliteOsWrite(OsFile * id,const void * pBuf,int amt);
页面是文件系统上最小的事务单元。当数据库需要从文件中读取数据时,它会将其作为页面请求。将页面加载到数据库引擎后,如果页面在其缓存上频繁访问,则可以存储该页面。页面编号并从一开始。第一页称为根页面。页面的大小是不变的。
/ *打开具有给定文件名和nPage最大缓存限制的寻呼机* / int sqlitepager_open(Pager ** ppPager,const char * zFilename,int nPage,int nEx);
/ *获取页码* / int sqlitepager_get 指定的页面(Pager * pPager,Pgno pgno,void ** ppPage);
/ *开始将数据写入pData * / int中 指定的页面sqlitepager_write(void * pData);
/ *提交页面更改到文件* / int sqlitepager_commit(Pager *);
/ *关闭文件连接* / int sqlitepager_close(Pager * pPager);
Btree是一种数据结构,用于根据数据的值将数据存储为树。最简单的BTree形式是二叉树。数据库使用Btree数据结构来存储索引以提高数据库的性能。Cursor是一个特殊的指针,用于指向以页面ID和偏移量idx给出的记录。
/ *打开文件连接到由zFileName与nCache的缓存大小指定的页面的文件名* / INT sqliteBtreeOpen(常量 字符 * zFilename,INT 模式,诠释 了nCache,B树 ** ppBtree)
/ *开始交易。该功能之前的任何B树修改操作应该叫* / INT sqliteBtreeBeginTrans(B树 * PBT)
/ *用nKey字节和值pData插入键pKey,将nData字节放入Btree * / int sqliteBtreeInsert(BtCursor * pCur,const void * pKey,int nKey,const void * pData,int nData)
/ *数据写入文件* / INT sqliteBtreeCommit(B树 * PBT)
/ *使用nKey字节将光标移动到匹配的pKey * / int sqliteBtreeMoveto(BtCursor * pCur,const void * pKey,int nKey,int * pRes)
VDBE是一个运行一组操作的虚拟机,由Code Generator生成。所有SQL命令包括插入,删除,更新,选择和转换为一组操作码,然后在此虚拟机上运行。每个操作码包含三个输入,命名为p1,p2和p3。您可以将此输入视为函数的输入。
下面是以下SQL select语句的示例执行操作码堆栈:
选择 *
来自 foo
WHERE value = “value” ;
0 |列数| 1 | 0 | 1 | COLUMNNAME | 0 | 0 |值2 | OPEN | 0 | 3 | FOO // OPEN光标和其指向3其中第三页IS根页foo的表(P3 IS NOT重要)
3 | verifycookie | 46 | 0 | //确保SCHEMA NOT changed4 | rewind | 0 | 11 | //转到第一个条目5 | COLUMN | 0 | 0 | //读取数据及将其推ON stack6 | COLUMN | 0 | 0 | 7 | NE | 1 | 10 | // 从堆栈中弹出TOP两个元素.IF它们不相等,然后跳转到指令p2。否则,继续下一条指令。
8 |列| 0 | 0 | 9 |回调| 1 | 0 | // 从堆栈中弹出P1 值并将它们组成一个数组10 |下一个| 0 | 5 | //将光标移动到下一条记录,如果数据退出转到P2,则转到下一行
11 |关闭| 0 | 0 | //关闭光标
Tokenizer,Parser和Code Generator在一起称为Compiler,它生成在VBDE上运行的操作码集。Tokenizer通过扫描SQL代码生成一组令牌。然后,它验证语法并生成解析树。代码生成器将此解析树转换为使用SQLite操作码编写的迷你程序。
SQLite是一个简单,轻量级,高性能的关系数据库,广泛用于软件设计。Eite版本的SQLite是用简单的架构和高度可读的代码编写的。寻呼机提供了一个抽象层,可以将数据作为固定大小的块读写到文件系统中。而Btree提供了一种以内存方式存储数据的方法,可以更快地访问数据。当SQL进入SQLite时,它将SQL转换为SQLite机器代码并在VBDE上运行它。结果通过API发回给用户。