SQLite在指定列后面插入字段_Sqlite 架构解析

1 架构概览

SQLite 把 SQL 语句在运行时编译成字节码 bytecode,然后使用虚拟机,控制字节码的执行。

使用 sqlite3_prepare_v2() 编译好的字节码被封装在 sqlite3_stmt 结构体中。一个 sqlite3_stmt 结构体对应一个 SQL 语句,可使用 sqlite3_reset() 终结当前语句的执行并重置语句到初始状态、通过绑定函数(sqlite3_bind_*)和步进函数(sqlite3_step)实现编译字节码复用。不再使用的sqlite3_stmt 可使用 sqlite3_finalize 销毁。

⚠️ SQLite 没有对 sqlite3_stmt 做缓存

d1cd049373726e6d1bd7875952702166.png
SQLite 架构图


1.1 分词器 Tokenizer

Tokenizer把 SQL 文本拆分成多个标号,并把它们一个接一个地提交给解析器。在 Sqlite 中,Tokenizer 调用 Parser (在 YACC 和 BISON 中,Parser 主动调用 Tokenizer),是线程安全的并且运行的更快。

1.2 解析器 Parser

Parser 根据上下文,把语义给予 Tokenizer 产生的各个标号,并构建解析树。SQLite 中的 Parser 是使用 Lemon parser generator 生成的。Lemon 所做的工作与 YACC/BISON 相同,但使用一个出错少的输入语法。Tokenizer 也是使用 Lemon 产生的,可重入且线程安全。

https://www.sqlite.org/src/doc/trunk/doc/lemon.html

1.3 字节码产生器 Code Generator

字节码产生器会分析 Parser 产生的解析树,产生 bytecode (存储在 prepared statement 中),进而把各个独立的字节码传递给虚拟机,由虚拟机控制字节码的执行,完成 SQL 语句所表示的行为。

在字节码产生器中有很多文件:attach.c, auth.c, build.c, delete.c, expr.c, insert.c, pragma.c, select.c, trigger.c, update.c, vacuum.c, where.c, wherecode.c, 和 whereexpr.c。

  • expr.c 处理表达式的字节码生成
  • where*.c 处理SELECT, UPDATE 和 DELETE的 WHERE 子句的字节码生成
  • 文件 attach.c, delete.c, insert.c, select.c, trigger.c update.c, 和 vacuum.c 处理对应名称的语句的字节码产生(可能调用expr.c 和 where.c)
  • 其它语句都由 build.c 控制生成字节码
  • auth.c 实现 sqlite3_set_authorizer()

1.4 查询优化器 query planner

代码产生器,尤其是 where*.c 和 select.c 中的逻辑,有时被称为查询优化器 query planner。 对于特定 SQL 语句,可能有成百上千种不同的算法去实现。query planner 可为特定的 SQL 语句选择最好的算法。

1.5 字节码引擎 Bytecode Engine

bytecode 程序运行在字节码虚拟机上,实现文件有:

  • vdbe.h 虚拟与 SQLite 库之间的接口,
  • vdbe.c 虚拟机主体实现文件
  • vdbeInt.h 定义虚拟机私有的接口和结构体
  • vdbeaux.c 包含被其它 SQLite library(如虚拟机和接口模块)使用的工具,用来构建 VM 程序
  • vdbeapi.c 定义外部使用虚拟机的接口,如sqlite3_bind_int() 和 sqlite3_step().
  • vdbe*.c 定义各种虚拟机帮助函数
  • vdbemem.c 各种数据类型(strings, integer, floating point numbers, and BLOBs) 在虚拟机中的存储对象,"Mem"
  • func.c SQLite 使用 C 历程实现的 SQL 函数,如abs(), count(), substr()
  • date.c 日期和时间函数
  • 自定义函数,如coalesce() and typeof(),由字节码产生器直接实现

1.6 B-Tree

在磁盘上,SQLite 使用 B-tree 维护数据(btree.c),一个表或索引对应一个独立 B-tree,所有的 B-tree 都在一个库文件中。数据库文件格式 file format 定义十分稳定,具有非常好的兼容性。B-tree 子系统接口和 SQLite 库的其它接口在 btree.h 中。

1.7 页缓存 Page Cache

B-tree 模块以固定大小的页,向磁盘请求信息,默认为 4096 字节,域值为 {512,65536}。Page Cache 负责页的读、写和缓存,提供回滚和原子提交抽象,负责库文件锁操作

B-tree 驱动器向 Page Cache 请求特定的页,并当它想修改页、提交或是回滚页时通知 Page Cache 。Page Cache 处理所有的细节,确保快速、安全且高效的完成请求。

Page Cache 主要在 pager.c 文件中实现;WAL mode 逻辑在 wal.c 中;内存缓存由 pcache.c 和 pcache1.c 共同实现;page cache子系统和SQLite 其它模块之间的接口定义在 pager.h 中。

1.8 操作系统接口 OS Interface

SQLite 使用抽象对象 VFS,保证跨系统的移植性。VFS 提供的方法有:opening, read, writing, 和 closing 磁盘上的文件;其它系统特定的任务,如 finding the current time, obtaining randomness to initialize the built-in pseudo-random number generator。

1.9 实用工具 Utilities

Memory allocation, caseless string comparison routines, portable text-to-number conversion routines, and other utilities are located in util.c.

由哈希表维护的解析器使用的标号表 in hash.c

The utf.c source file contains Unicode conversion subroutines.

SQLite has its own private implementation of printf() (with some extensions) in printf.c

伪随机数产生器 (PRNG) in random.c

https://huili.github.io/sqlite/architecture.html

2 SQL(Structured Query Language)

SQL 是面向业务的,为处理复杂的关系而存在;NoSQL 是面向数据的,为处理海量数据而生。这是 SQL 和 NoSQL 之间的本质差别。

在 SQLite 中,分词器、解析器、字节码生成器和虚拟机等模块的大部分工作,都是为了处理 SQL 语句中的结构化信息,处理应用层委托到 SQLite 的业务逻辑。

2.1 语句解析

2.2 建立连接

2.3 关闭连接

2.4 查询

2.5 更新

2.6 插入

3 B-Tree 与

-Tree

SQLite 使用 B-Tree 存储索引,一个 B-Tree 对应一个索引;使用

-Tree 存储表,一个
-Tree 对应一个表。Tree 创建时就会为其分配一个根 Page(对应一个 Page Number),Page 标识了该 Tree,Tree 的根 Page 在 其生命周期内不会发生变更。

3.1 B-tree 索引

在基于 DBMSs 的外存储领域中,B-tree 在一个非常重要的索引结构,按键所对应的值有序地存储记录集合。B-tree 是一种特殊的高度平衡树, n-ary, n > 2,所有叶子节点必须在统一层上。

实体(Entries[*])和搜索信息(如 key values)可存储在 B-tree 的内部节点和叶子节点。

B-tree在所有的树操作(即插入、删除、搜索和下一个搜索)中,提供了近乎最佳的性能。

为了避免混淆,我在这里使用术语“entry”表示元组或记录。entry 由键和其它可选数据组成

3.2

-Tree 表

-Tree 是 B-tree 的变种,
所有的实体 entries 都在叶子节点上,内部节点只包含有序的搜索信息(key values)和子节点指针。entries 是 (key value, data value) 对,按 key 值排序。

2ab035e44cc4eff15743a0672bc3aa19.png
B+-Tree 内部节点结构

93238a4eedd68de576ff58e8ce31aa55.png
B+-Tree 页架构(带有益处页)

B-Tree 和

-Tree 的内部页节点的子指针数量,在一个预设的范围内
[lower, upper](2 * lower >= upper),都是可变的。根页节点可能不符合这个规则,可能有任何数量的子指针即 [0, upper]所有的叶子节点都在同一个最低层上,可以构成一个有序的链表。

upper = n +1 的

-Tree ,内部节点最多有n 个 keys,n+1 个子节点指针。对于任何内部节点,其对空间划为为
[<= Key(0), Key (0) < & <=Key(1), ..., Key (n - 1)<]。这样查询任何一条记录 只需要遍历 O(log m) (m 是树中所有实体的数量)个节点

3.3 B-tree 承载结构

  • struct Btree:一个数据库连接(sqlite3 指针)对应一个 struct Btree 对象,指向共享的struct BtShared 对象。sqlite3.mutex 为其各个字段的访问提供线程安全。如果开启共享缓存,使用 BtLock lock 字段控制首页的安全。
  • struct MemPage:页加载到内存中后,pager 会创建一个 MemPage 对象并把其前8个字节设置为0,使用磁盘上的原始页的内容信息填充该对象。通过 BtShared *pBt 互斥信号为其提供线程安全;DbPage *pDbPage 字段是控制页的具柄。
  • struct BtLock:存储在 BtShared.pLock 中的锁列表。使用表 BtShared.iTable 的根页打开游标时,向 BtShared.pLock列表添加一个 BtLock 对象,事物提交、回滚后或 Btree 关闭后移除该对象
  • BtCursor:一个指向 b-tree 中的特定记录 (entry) 的指针。通过 MemPage 和 MemPage 中记录的下标识别记录。一个 BtCursor 对应一个 Btree,BtCursor 列表存储在 BtShared 中;BtShared.mutex 为字段访问提供安全;
  • struct CellInfo:持有一个字段的信息,parseCellPtr() 函数使用之间从磁盘读出来的信息填充该字段
  • struct BtShared:一个对象代表一个数据库文件

b5cbc81c7fd228870bfa14265aa54a35.png

df9be3a1dafe389683ee070e39e10c07.png

3.4 数据库连接对象 sqlite3 设置数据源 Btree

struct Btree 面向业务层的连接概念,为单个连接提供服务。一个 sqlite3 对象代表一个连接,对应一个为其提供数据的 struct Btree 对象。

struct Btree {
  sqlite3 *db;       /* The database connection holding this btree */
  BtShared *pBt;     /* Sharable content of this btree */
  ...
  Btree *pNext;      /* List of other sharable Btrees from the same db */
  Btree *pPrev;      /* Back pointer of the same list */
#ifndef SQLITE_OMIT_SHARED_CACHE
  BtLock lock;       /* Object used to lock page 1 */
#endif
};

struct BtShared {
  ...
  BtShared *pNext;      /* Next on a list of sharable BtShared structs */
  ...
};

int sqlite3BtreeOpen(
  sqlite3_vfs *pVfs,       /* VFS to use with this b-tree */
  const char *zFilename,   /* Name of database file to open */
  sqlite3 *db,             /* Associated database connection */
  Btree **ppBtree,         /* Return open Btree* here */
  int flags,               /* Flags */
  int vfsFlags             /* Flags passed through to VFS open */
);

sqlite3BtreeOpen 会根据数据库连接对象 sqlite3 和 虚拟文件系统等5个入参,创建一个 Btree 对象,填充各个字段,返回 Btree 对象。

 for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){
        assert( pBt->nRef>0 );
        if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager, 0))
                 && sqlite3PagerVfs(pBt->pPager)==pVfs ){
          ...
          p->pBt = pBt;
          pBt->nRef++;
          break;
        }
      }
      ...
    }

同一虚拟文件系统中的相同文件目录确定一个 BtShared 对象。创建好的 BtShared 会放如一个静态额全局列表中 static BtShared * sqlite3SharedCacheList,通过 sqlite3SharedCacheList 实现 BtShared 文件数据共享。

3.5 断开数据源 Btree

static int connectionIsBusy(sqlite3 *db){
  int j;
  assert( sqlite3_mutex_held(db->mutex) );
  if( db->pVdbe ) return 1;
  for(j=0; j<db->nDb; j++){
    Btree *pBt = db->aDb[j].pBt;
    if( pBt && sqlite3BtreeIsInBackup(pBt) ) return 1;
  }
  return 0;
}
int sqlite3BtreeClose(Btree*);

关闭数据库涉及到多模块:虚拟表、虚拟机、B-Tree、游标、日志、Pager、Schema 等。

  1. 断开虚拟表。
  2. 通过 sqlite3_close 关闭数据库连接:有未完成的字节码程序或是 pager 正在进行数据库备份(connectionIsBusy),此时立即返回 SQLITE_BUSY;通过 sqlite3_next_stmt 逐个取出未完成的字节码程序;通过 sqlite3_finalize 终止字节码程序的执行;
  3. 或通过 sqlite3_close_v2 强制关闭数据库连接:数据库连接对象变成一个僵尸对象(有未完成语句或是正在备份数据,导致资源泄露),返回成功标志。
  4. 调用 sqlite3LeaveMutexAndCloseZombie 清理资源。

3.5 Btree

Btree 模块结构化表和索引,为虚拟机提供结构化的数据服务,为 SQL 语句提供结构支撑(虚拟机为 SQL 语句提供控制逻辑)。Btree 向上为 虚拟机提供服务,向下为按页加载的原始数据提供结构化信息。下面详细介绍 Btree 中基于 Pager 的事务和游标,及其插入、删除、更新等操作。

struct Btree {
  sqlite3 *db;       /* The database connection holding this btree */
  BtShared *pBt;     /* Sharable content of this btree */
  u8 inTrans;        /* TRANS_NONE, TRANS_READ or TRANS_WRITE */
  u8 sharable;       /* True if we can share pBt with another db */
  u8 locked;         /* True if db currently has pBt locked */
  u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */
  int wantToLock;    /* Number of nested calls to sqlite3BtreeEnter() */
  int nBackup;       /* Number of backup operations reading this btree */
  u32 iDataVersion;  /* Combines with pBt->pPager->iDataVersion */
  Btree *pNext;      /* List of other sharable Btrees from the same db */
  Btree *pPrev;      /* Back pointer of the same list */
#ifndef SQLITE_OMIT_SHARED_CACHE
  BtLock lock;       /* Object used to lock page 1 */
#endif
};

3.5.1 事务

事务依靠事务栈维护状态。事务分为通过 BEGIN 开启的正常事务,和通过 savepoint 开启的语句子事务。语句子事务不能独立存在于栈底,但可嵌套。BEGIN 事务通过 COMMIT提交。语句子事务通过 RELEASE 提交。BEGIN 事务通过 ROOLBACK 回滚。语句子事务通过 ROOLBACK TO 提交。

int sqlite3BtreeCommit(Btree *p){
  int rc;
  sqlite3BtreeEnter(p);
  rc = sqlite3BtreeCommitPhaseOne(p, 0);
  if( rc==SQLITE_OK ){
    rc = sqlite3BtreeCommitPhaseTwo(p, 0);
  }
  sqlite3BtreeLeave(p);
  return rc;
}

两阶段提交:变更写入磁盘,删除日志文件或截断或头清零以提交事务。

写入可见性:

读写锁转换

https://www.sqlite.org/lang_transaction.html

https://sqlite.org/lang_savepoint.html

3.5.2 游标

https://sqlite.org/vtab.html

3.8 B-tree 覆盖索引

4 Pager

10b2a34c2577de6015dbb7a12cfa670a.png

7 文件格式

5 操作系统接口

6 内存管理

8 锁与多线程

9 事物与可见性

https://sqlite.org/arch.html

https://huili.github.io/sqlite/architecture.html

Inside SQLite

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值