前言
      我们知道任何一种关系型数据库管理系统都支持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. CSharpGL(7)对VAO和VBO的封装

    CSharpGL(7)对VAO和VBO的封装 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码中包含10多个独立的Demo,更适合入门参考 ...

  2. Go语言实战 - 网站性能优化第一弹“七牛云存储”

    由于用户纷纷反应山坡网的打开速度比较慢,所以两天前我们决定把服务器从linode迁移到阿里云. 整个迁移过程非常平滑,基本上一个小时就完成了.而且阿里云的配套设施提供的也很不错,运行状态监控什么的都有 ...

  3. js ajax php分页组件

    github  https://github.com/lihefen/pageList html页面 <!DOCTYPE html><html><head>< ...

  4. JavaScript随笔5

    事件(1) 鼠标的点击坐标: 火狐不支持 IE event.clientX//可视区坐标 event.clientY FF ev.clientX ev.clientY 兼容: var oEvent = ...

  5. 在 CSS 预编译器之后:PostCSS

    提到css预编译器(css preprocessor),你可能想到Sass.Less以及Stylus.而本文要介绍的PostCSS,正是一个这样的工具:css预编译器可以做到的事,它同样可以做到. “ ...

  6. ASP.NET MVC5+EF6+EasyUI 后台管理系统(4)-创建项目解决方案

    系列目录 前言 为了符合后面更新后的重构系统,文章于2016-11-1日重写 设计中术语,概念这种东西过于模糊,我们必须学习累积才能认识这些概念模型. 我无法用文章来下详细解析此系统的深层概念,需要大 ...

  7. 简单动态规划-LeetCode198

    题目:House Robber You are a professional robber planning to rob houses along a street. Each house has ...

  8. Cesium原理篇:Batch

    通过之前的Material和Entity介绍,不知道你有没有发现,当我们需要添加一个rectangle时,有两种方式可供选择,我们可以直接添加到Scene的PrimitiveCollection,也可 ...

  9. react-native ListView使用详解

    刚好今天七夕,呆萌的程序猿没有妹纸,刚好发小明天结婚,我还在异地,晚上还要苦逼的赶火车.趁着下午比较闲,更新一下Blog,也算是在百无聊赖之时给众多单身程序猿们的小福利吧,虽然已经好久没更了...囧 ...

  10. 解析ListView联动的实现--仿饿了么点餐界面

    一.博客的由来 大神王丰蛋哥 之前一篇博客仿饿了点餐界面2个ListView联动(http://www.cnblogs.com/wangfengdange/p/5886064.html) 主要实现了2 ...