前言
      我们知道任何一种关系型数据库管理系统都支持SQL(Structured Query Language),相对于文件管理系统,用户不用关心数据在数据库内部如何存取,也不需要知道底层的存储结构,熟悉SQL,就能熟练使用数据库。SQL的引入,使得数据库系统需要将SQL转换为内部的数据结构,然后与底层的存储结构打通,达到用户存取数据的目的。所谓的SQL对应的数据结构,我们通常称之为执行计划,每个SQL执行前,都需要生成执行计划,然后执行。SQL如何变化到等价的执行计划?我们熟悉的数据库,Oracle,Sqlserver,Mysql等通过对SQL进行词法分析,语法分析,语义分析,生成执行计划等步骤,最终生成执行计划,这个计划一般是一个复杂的数据结构。SQLite也通过以上几步生成执行计划,但特别的是,SQLite的执行计划是一串指令流,这个指令流是由代码生成器生成,代码生成器将语法树翻译成一种SQLite专用的内部指令,通过虚拟机来解析执行。指令流相当于SQL与虚拟机的中介,由于指令流是扁平的,SQLite提供方法(PRAGMA vdbe_trace=ON)让用户可以看到执行SQL的每一条指令,清楚地知道数据在SQLite内部是如何流转的。本文主要讲SQLite的虚拟机(Virtual Database Engine,简称VDBE)的原理以及相关的内部指令。

虚拟机
     所谓虚拟机是指对真实计算机资源环境的一个抽象,它为语言程序提供了一套完整的计算机接口。比如我们熟悉的JAVA语言,我们在跑JAVA程序时,其实是运行在JVM(JAVA Virtual Machine)环境中,所有的JAVA程序首先被编译为.class类文件,这种类文件在虚拟机上执行,也就是说class文件并不与操作系统指令对应,而是经过虚拟机间接与操作系统交互。SQLite的虚拟机也是如此,编译SQL产生的指令流只有SQLite虚拟机(Virtual Database Engine,简称VDBE)能识别,由虚拟机与底层的存储(表,索引)交互,这种方式使得SQLite内部模块分工非常清晰,耦合度很低。如下图所示,我们可以看到VDBE的位置,它处于编译器与Btree模块的中间,是SQLite的核心,负责SQL到数据存取的交互。后面我提到的虚拟机都是指SQLite虚拟机(Virtual Machine,VM),VM模块将底层存储看作是记录维度的文件系统,通过执行指令流,来读写表上的记录。


VDBE数据结构和API

struct Vdbe{
sqlite3 *db;   /* The database connection that owns this statement */
Op *aOp;     /* Space to hold the virtual machine's program */
int nOp;     /* Number of instructions in the program */
Mem **apArg; /* Arguments to currently executing user function */
Parse *pParse; /* Parsing context used to create this Vdbe */
int pc; /* The program counter */
Mem *aMem; /* The memory locations */
int nMem; /* Number of memory locations currently allocated */
Mem *aColName; /* Column names to return */
u16 nResColumn; /* Number of columns in one row of the result set */
char *zSql; /* Text of the SQL statement that generated this */
}

我从源码中选取了比较重要的对象,主要包括数据库对象(db),指令流对象(aOp,nOp),绑定输入的参数值(apArg),解析SQL的对象(pParse),指令流计数器(pc),存储临时变量的寄存器(aMem,nMem),返回结果集集的列名和列信息(aColName,nResColumn)以及执行的产生虚拟机指令的SQL(zSql)等。这些基本就是虚拟机对象的全部,有指令,有寄存器,有指令计数器,与汇编语言非常相似,只不过VDBE里面的指令是sqlite内部识别的指令,而汇编语言指令是与机器指令对应的。如果想了解VDBE所有的对象,可以参考vdbeInt.h中关于该结构的定义,另外关于sqlite3结构和Parse结构可以参考sqliteInt.h文件。
     了解了Vdbe数据结构,我们再来看看我们平时常用的API是如何与VDBE交换数据的。通常我们要执行一个语句,会执行如下几个步骤。
1.调用sqlite3_prepare_*来编译生成指令流,返回一个sqlite3_stmt对象,其实这个对象就是vdbe对象。
2.调用sqlite3_bind_*来将参数传递给vdbe,
3.调用sqlite3_step进行执行,这时候会启动虚拟机执行一条条指令,直到遇到中断或者停止指令为止
4.调用sqlite3_column_*来获取上一步准备好的结果集
5.调用sqlite3_finalize,销毁vdbe对象,结束这次执行。
此外我们还可能用到sqlite3_reset接口,这个接口将指令流回退到第一条指令,用户可以调用sqlite3_step重新执行。有关API的详细说明,可以参考文件vdbeapi.c。

虚拟机指令
      虚拟机核心就是扁平化指令,SQLite定义了一系列指令语言,每个指令做一小部分动作,虚拟机通过执行一些列指令达到查询,修改数据库的目的。每一条指令包含一个操作符和5个操作数,形式如下:<opcode,P1,P2,P3,P4,P5>。P1,P2,P3是一个32位有符号整数,P1一般是游标编号,P2一般是指令需要跳转的指令位置,P4是一个32位/64位整数,64位的浮点数,或者是指向字符串的指针,或者是二进制等,P5是一个无编号的字符。不是每条指令都使用了全部5个操作数,有的指令只需要2到3个操作数。后面一篇文章我会结合实例详细讲解指令的作用,以及对应操作数的含义。

虚拟机执行流程
      虚拟机的核心流程在sqlite3VdbeExec函数中,我们调用sqlite3_step时就会调用到该函数。由于这个函数比较大,大概有6000行代码,里面包含了每条指令的执行过程,为了方便说明,我会简化函数内容来说明这个函数的逻辑,抽象的代码如下。从代码流程来看,逻辑非常简单,通过循环遍历指令数组中的每条指令逐一执行,直到遇到中断或终止指令为止。如果需要逐条了解每条指令的含义,还需要仔细阅读代码。

sqlite3VdbeExec(Vdbe *p)
{
  Op *aOp = p->aOp; /* Copy of p->aOp */
  Op *pOp = aOp; /* Current operation */   for(pOp=&aOp[p->pc]; rc==SQLITE_OK; pOp++){
    switch(pOp->opcode){
    case OP_Goto: //jump to P2指向的指令
    {
      pOp = &aOp[pOp->p2 - ];
      break;
    }
    case OP_Integer: // value P1 is written into register P2.
    {
      pOut = out2Prerelease(p, pOp);
      pOut->u.i = pOp->p1;
      break;
    }
    case OP_Real:
    {
      ......
      break;
    }
    case OP_Halt:
    {
      ......
      break;
    }
    ...
   }// end of switch
} // end of for
}

小结
     本文介绍了SQLite虚拟机以及对应的指令流。通过介绍vdbe的存储结构,我们了解到vdbe对象所包含的内容;通过介绍API,我们了解到API与虚拟机的关系;通过介绍函数sqlite3VdbeExec的实现,我们知道虚拟机执行流程非常清晰,通过执行一系列指令流,就可以实现查询,更新数据。

SQLite学习笔记(十一)&&虚拟机原理的更多相关文章

  1. Sqlite学习笔记(四)&&SQLite-WAL原理

    Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优势,这篇文章将会详细分析WAL的原理,做到知其然,更要知其所以然. WAL是 ...

  2. Sqlite学习笔记(四)&&SQLite-WAL原理(转)

    Sqlite学习笔记(三)&&WAL性能测试中列出了几种典型场景下WAL的性能数据,了解到WAL确实有性能优势,这篇文章将会详细分析WAL的原理,做到知其然,更要知其所以然. WAL是 ...

  3. SQLite学习笔记(七)&&事务处理

    说到事务一定会提到ACID,所谓事务的原子性,一致性,隔离性和持久性.对于一个数据库而言,通常通过并发控制和故障恢复手段来保证事务在正常和异常情况下的ACID特性.sqlite也不例外,虽然简单,依然 ...

  4. openstack学习笔记一 虚拟机启动过程代码跟踪

    openstack学习笔记一 虚拟机启动过程代码跟踪 本文主要通过对虚拟机创建过程的代码跟踪.观察虚拟机启动任务状态的变化,来透彻理解openstack各组件之间的作用过程. 当从horizon界面发 ...

  5. 机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理、源码解析及测试

    机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理.源码解析及测试 关键字:决策树.python.源码解析.测试作者:米仓山下时间:2018-10-2 ...

  6. python3.4学习笔记(十一) 列表、数组实例

    python3.4学习笔记(十一) 列表.数组实例 #python列表,数组类型要相同,python不需要指定数据类型,可以把各种类型打包进去#python列表可以包含整数,浮点数,字符串,对象#创建 ...

  7. Go语言学习笔记十一: 切片(slice)

    Go语言学习笔记十一: 切片(slice) 切片这个概念我是从python语言中学到的,当时感觉这个东西真的比较好用.不像java语言写起来就比较繁琐.不过我觉得未来java语法也会支持的. 定义切片 ...

  8. SQLite 学习笔记

    SQLite 学习笔记. 一.SQLite 安装    访问http://www.sqlite.org/download.html下载对应的文件.    1.在 Windows 上安装 SQLite. ...

  9. sqlite学习笔记7:C语言中使用sqlite之打开数据库

    数据库的基本内容前面都已经说得差点儿相同了.接下看看如何在C语言中使用sqlite. 一 接口 sqlite3_open(const char *filename, sqlite3 **ppDb) 打 ...

随机推荐

  1. BootStrap_03之组件(手风琴、导航)

    1.BootStrap组件--按钮组: .btn-group>.btn*5: .btn-group-justified: .btn-group-lg/sm/xs: .btn-group-vert ...

  2. lr文件下载脚本(文件参数化重命名)

    http://wenku.baidu.com/link?url=6oiIadyF9eFS4VshKbfJDnxrBh2IX919ndi0JO8yoqTRNRNIpavFrZJ9LPVb-FBSfbRY ...

  3. MVC5 网站开发之七 用户功能 3用户资料的修改和删除

    这次主要实现管理后台界面用户资料的修改和删除,修改用户资料和角色是经常用到的功能,但删除用户的情况比较少,为了功能的完整性还是坐上了.主要用到两个action "Modify"和& ...

  4. 实践 Neutron FWaaS - 每天5分钟玩转 OpenStack(118)

    前面我们学习了 FWaaS 的理论知识,今天将通过实验来学习 FWaaS. 在我们的实验环境中,有两个 instance: cirros-vm1(172.16.100.3) 和 cirros-vm2( ...

  5. 数据库日常维护-CheckList_02有关数据库备份检查

    数据库备份是DB日常运维中最基本的也是最重要的工作,很多情况下都是做成作业形式实现自动化周期性的做全备.差异以及日志备份.那么,如果作业出现问题没有完成工作,我们可以设置自动报警如email被动提醒我 ...

  6. Oracle使用SQL传输表空间

    源环境:RHEL 6.4 + Oracle 11.2.0.4 目的环境:RHEL 6.4 + Oracle 11.2.0.4 DG双机 要求:使用SQL传输表空间DBS_D_JINGYU从源环境到目的 ...

  7. 微信JS-SDK坐标位置转换为百度地图坐标

    微信JS-SDK开发过程中,使用getLocation获取坐标位置,如何将微信获取的坐标直接应用到百度地图中,显示以下效果: 说明:红色图标是从微信转换过来的位置,蓝色图标是周边位置.首先从微信开发流 ...

  8. 深入理解定时器系列第一篇——理解setTimeout和setInterval

    × 目录 [1]setTimeout [2]setInterval [3]运行机制[4]作用[5]应用 前面的话 很长时间以来,定时器一直是javascript动画的核心技术.但是,关于定时器,人们通 ...

  9. C#实现微信开发前奏

    不想废话,直接写了!因为是留给自己做随笔的,所以大神们看到别喷…… 1.必须有微信公众账号 2.你也可以申请测试微信号,链接给你   http://mp.weixin.qq.com/debug/cgi ...

  10. Monkey Patch/Monkey Testing/Duck Typing/Duck Test

    Monkey Patch Monkey Testing Duck Typing Duck Test