前言
      我们知道任何一种关系型数据库管理系统都支持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. Flux 普及读本

    话说当时做 APP 时,三月不知肉味,再次将眼光投放前端,有种天上一天,地下一年的感觉. Flux 是一种思想 了解的最好方式当然是看Flux官方文档了.React 中文站点也能找到对应的翻译版本,但 ...

  2. HTML5网页录音和压缩,边猜边做..(附源码)

    宣传一下自己的qq群: (暗号:C#交流) 欢迎喜欢C#,热爱C#,正在学习C#,准备学习C#的朋友来这里互相学习交流,共同进步 群刚建,人不多,但是都是真正热爱C#的 我也是热爱C#的 希望大家可以 ...

  3. ajax基础学习

    AJAX即"Asynchronous JavaScript and XML",意思是异步JavaScript和XML,是指一种创建交互式网页的网页开发技术. 虽然现在很少有人去自己 ...

  4. Vue ES6 Jade Scss Webpack Gulp

    一直以来非常庆幸曾经有翻过<代码大全2>:这使我崎岖编程之路少了很多不必要的坎坷.它在软件工艺的话题中有写到一篇:"首先是为人写程序,其次才是机器(Write Programs ...

  5. Win下最爱效率利器:AutoHotKey

    AutoHotkey是一个windows下的开源.免费.自动化软件工具.它由最初旨在提供键盘快捷键的脚本语言驱动(称为:热键),随着时间的推移演变成一个完整的脚本语言.但你不需要把它想得太深,你只需要 ...

  6. CSS系列目录

    1.  在HTML中引入CSS的方法 2.  CSS选择器 2.1 CSS3新增选择器 3.  CSS的继承与层叠特性 4.  CSS中盒子模型 5.  CSS中盒子之间的关系 6.  CSS中盒子的 ...

  7. angular + easyui 做界面验证

    angular结合easyui这事其实并不是很合适,因为:angular的特点之一是双向绑定,页面元素与页面逻辑之间解耦:easyui是对页面元素进行封装,甚至一些组件是隐藏了原本的dom元素,初始化 ...

  8. Cesium原理篇:Property

    之前主要是Entity的一个大概流程,本文主要介绍Cesium的属性,比如defineProperties,Property(ConstantProperty,CallbackProperty,Con ...

  9. 从N个元素的集合中随机取m个元素的算法实现

    最近有一个需求,比较简单,就是如标题所说的,从N个元素中随机取m个元素,当然这m个元素是不能存在重复的.本以为这么简单的需求,应该有现成的工具类来实现,但是几次查找居然没找到(有知道的可以推荐下哈^_ ...

  10. TCP四种计时器

    TCP共使用以下四种计时器,即重传计时器.坚持计时器.保活计时器和时间等待计时器 .这几个计时器的主要特点如下:      1.重传计时器      当TCP发送报文段时,就创建该特定报文段的重传计时 ...