SQLite学习笔记(十二)&&虚拟机指令
上篇文章简单讨论了虚拟机的原理,这篇文章我们详细讨论下指令,具体从几种典型的SQL语句来看看每种SQL对应的指令流,以及每个指令的含义。通过explain语句,可以看到语句对应的指令流;通过pragma vdbe_trace=on指令,我们甚至可以得到语句对应的指令执行流程,包括跳转等。
测试表结构
CREATE TABLE t1(
id integer primary key autoincrement,
user_id int,
c1 varchar(1000),
c2 varchar(1000)
);
测试语句
(1)INSERT
sqlite> explain insert into t1(user_id,c1,c2) values(1111,'abc','abc');
0|Init|0|17|0||00|Start at 17
1|OpenWrite|0|5|0|4|00|root=5 iDb=0; t1
2|NewRowid|0|4|2||00|r[]=rowid
3|MemMax|2|4|0||00|r[]=max(r[],r[])
4|SoftNull|5|0|0||00|r[]=NULL
5|Integer|1111|6|0||00|r[]=1111
6|String8|0|7|0|abc|00|r[]='abc'
7|String8|0|8|0|abc|00|r[]='abc'
8|MakeRecord|5|4|9|DDBB|00|r[]=mkrec(r[5..8])
9|Insert|0|9|4|t1|1b|intkey=r[] data=r[]
10|Close|0|0|0||00|
11|OpenWrite|0|3|0|2|00|root=3 iDb=0; sqlite_sequence
12|NotNull|3|14|0||00|if r[]!=NULL goto 14
13|NewRowid|0|3|0||00|r[]=rowid
14|MakeRecord|1|2|10||00|r[]=mkrec(r[1..2])
15|Insert|0|10|3||08|intkey=r[] data=r[]
16|Halt|0|0|0||00|
17|Transaction|0|1|4|0|01|
18|TableLock|0|5|1|t1|00|iDb=0 root=5 write=1
19|TableLock|0|3|1|sqlite_sequence|00|iDb=0 root=3 write=1
20|OpenRead|0|3|0|2|00|root=3 iDb=0; sqlite_sequence
21|Null|0|2|3||00|r[2..3]=NULL
22|String8|0|1|0|t1|00|r[]='t1'
23|Rewind|0|31|0||00|
24|Column|0|0|2||00|r[]=
25|Ne|1|29|2||10|if r[]!=r[] goto 29
26|Rowid|0|3|0||00|r[]=rowid
27|Column|0|1|2||00|r[]=
28|Goto|0|31|0||00|
29|Next|0|24|0||00|
30|Integer|0|2|0||00|r[]=0
31|Close|0|0|0||00|
32|Goto|0|1|0||00|
通过explain我们得到了语句对应的指令流,该语句总共包含了32条指令,下面我们逐条来说明指令的含义。
|
指令 |
含义 |
0 |
Init|0|17|0||00|Start at 17 |
指令从P2(17)开始 |
1 |
OpenWrite|0|5|0|4|00|root=5 iDb=0; t1 |
打开表t1读写游标cursor0,P4,表示总共有4列 |
2 |
NewRowid|0|4|2||00|r[4]=rowid |
生成一个newRowid,写入P2寄存器,P3寄存器存储的是目前最大值,当rowid达到最大值,则SQLITE_FULL |
3 |
MemMax|2|4|0||00|r[2]=max(r[2],r[4]) |
取出sqlite_sequence里面和表里面记录的最大值 |
4 |
SoftNull|5|0|0||00|r[5]=NULL |
初始化r[5]为null,rowid列 |
5 |
Integer|1111|6|0||00|r[6]=1111 |
设置寄存器r[6]为1111 |
6 |
String8|0|7|0|abc|00|r[7]='abc' |
设置寄存器r[7]为abc |
7 |
String8|0|8|0|abc|00|r[8]='abc' |
设置寄存器r[8]为abc |
8 |
MakeRecord|5|4|9|DDBB|00|r[9]=mkrec(r[5..8]) |
生成t1表记录 将寄存器r[5..8]的内容转为记录格式,存入r[9],P2指定长度为4。 |
9 |
Insert|0|9|4|t1|1b|intkey=r[4] data=r[9] |
插入 key在r[4]中,data在r[9]寄存器中 |
10 |
Close|0|0|0||00| |
关闭游标 |
11 |
OpenWrite|0|3|0|2|00|root=3 iDb=0; |
打开写游标 |
12 |
NotNull|3|14|0||00|if r[3]!=NULL goto 14 |
r[3]不为null,则跳转14 |
13 |
NewRowid|0|3|0||00|r[3]=rowid |
生成新的rowid |
14 |
MakeRecord|1|2|10||00|r[10]=mkrec(r[1..2]) |
生成sqlite_sequence记录 生成记录,r[1]=t1,r[2]=seq |
15 |
Insert|0|10|3||08|intkey=r[3] data=r[10] |
插入 key=r[3],data=r[10] |
16 |
Halt|0|0|0||00| |
终止 |
17 |
Transaction|0|1|4|0|01| |
打开一个新事务,创建回滚日志文件 |
18 |
TableLock|0|5|1|t1|00|iDb=0 root=5 |
上t1表锁(仅用于shared-cache) |
19 |
TableLock|0|3|1|sqlite_sequence|00|iDb=0 |
上sqlite_sequence表锁 |
20 |
OpenRead|0|3|0|2|00|root=3 iDb=0; |
打开sqlite_sequence表,P4,表示总共2列 |
21 |
Null|0|2|3||00|r[2..3]=NULL |
初始化寄存器为NULL |
22 |
String8|0|1|0|t1|00|r[1]='t1' |
设置r[1]为t1 |
23 |
Rewind|0|31|0||00| |
重置游标cursor P1,游标指向索引的第一个位置,如果tree为空,则跳转到31,否则执行下面的指令 |
24 |
Column|0|0|2||00|r[2]= |
获取cusor P1指向记录的P2th列,即sqlite_sequence的name列,结果存在r[2]中 |
25 |
Ne|1|29|2||10|if r[1]!=r[2] goto 29 |
如果sqlite_sequence表中记录与当前的表名不一致,跳转到29 |
26 |
Rowid|0|3|0||00|r[3]=rowid |
获取当前记录的rowid |
27 |
Column|0|1|2||00|r[2]= |
获取当前记录的第1列,结果存放在r[2]中,sqlite_sequence的第一列是seq值 |
28 |
Goto|0|31|0||00| |
跳转到31 |
29 |
Next|0|24|0||00| |
游标往后移,如果还有记录,则跳转到P2(24),否则继续往后执行。 |
30 |
Integer|0|2|0||00|r[2]=0 |
这是r[2]为0 |
31 |
Close|0|0|0||00| |
关闭sqlite_sequence表的游标 |
32 |
Goto|0|1|0||00| |
跳转到1,开始执行插入表t1 |
通过pragma命令,设置vdbe_trace为on可以看到SQL语句对应的指令流是如何运行的,具体如下:可以看到,指令并不是顺序执行的,而是存在跳转,具体的执行顺序,由代码生成器生成指令流和指令的内容决定。
sqlite> pragma vdbe_trace=on;
sqlite> insert into t1(user_id,c1,c2) values(1111,'abc','abc');
SQL: [insert into t1(user_id,c1,c2) values(1111,'abc','abc');]
VDBE Trace:
0 Init 0 17 0 00 Start at 17
17 Transaction 0 1 4 0 01
18 TableLock 0 5 1 t1 00 iDb=0 root=5 write=1
19 TableLock 0 3 1 sqlite_sequence 00 iDb=0 root=3 write=1
20 OpenRead 0 3 0 2 00 root=3 iDb=0; sqlite_sequence
21 Null 0 2 3 00 r[2..3]=NULL
REG[] = NULL
22 String8 0 1 0 t1 00 r[]='t1'
REG[] = t2[t1](8)
23 Rewind 0 31 0 00
24 Column 0 0 2 00 r[]=
REG[] = s4[user](8)
25 Ne 1 29 2 10 if r[]!=r[] goto 29
REG[] = t2[t1](8)
REG[] = s4[user](8)
29 Next 0 24 0 00
24 Column 0 0 2 00 r[]=
REG[] = s6[orders](8)
25 Ne 1 29 2 10 if r[]!=r[] goto 29
REG[] = t2[t1](8)
REG[] = s6[orders](8)
29 Next 0 24 0 00
24 Column 0 0 2 00 r[]=
REG[] = s2[t1](8)
25 Ne 1 29 2 10 if r[]!=r[] goto 29
REG[] = t2[t1](8)
REG[] = s2[t1](8)
26 Rowid 0 3 0 00 r[]=rowid
REG[] = i:3
27 Column 0 1 2 00 r[]=
REG[] = i:113
28 Goto 0 31 0 00
31 Close 0 0 0 00
32 Goto 0 1 0 00
1 OpenWrite 0 5 0 4 00 root=5 iDb=0; t1
2 NewRowid 0 4 2 00 r[]=rowid
REG[] = i:113
REG[] = i:114
3 MemMax 2 4 0 00 r[]=max(r[],r[])
REG[] = i:114
4 SoftNull 5 0 0 00 r[]=NULL
5 Integer 1111 6 0 00 r[]=1111
REG[] = i:1111
6 String8 0 7 0 abc 00 r[]='abc'
REG[] = t3[abc](8)
7 String8 0 8 0 abc 00 r[]='abc'
REG[] = t3[abc](8)
8 MakeRecord 5 4 9 DDBB 00 r[]=mkrec(r[5..8])
REG[] = s13[05000213130457616263616263......Wabcabc](8)
9 Insert 0 9 4 t1 1B intkey=r[] data=r[]
REG[] = s13[05000213130457616263616263......Wabcabc](8)
REG[] = i:114
10 Close 0 0 0 00
11 OpenWrite 0 3 0 2 00 root=3 iDb=0; sqlite_sequence
12 NotNull 3 14 0 00 if r[]!=NULL goto 14
REG[] = i:3
14 MakeRecord 1 2 10 00 r[]=mkrec(r[1..2])
REG[] = s6[031101743172...t1r](8)
15 Insert 0 10 3 08 intkey=r[] data=r[]
REG[] = s6[031101743172...t1r](8)
REG[] = i:3
16 Halt 0 0 0 00
(2)SELECT
sqlite> explain select rowid,user_id,c2 from t1 where rowid=112;
0|Init|0|12|0||00|Start at 12
1|OpenRead|0|5|0|4|00|root=5 iDb=0; t1
2|Explain|0|0|0|SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)|00|
3|Integer|112|1|0||00|r[1]=112
4|MustBeInt|1|10|0||00|
r[p1]中的值必需为int,或可以转为int,如果不能跳转到10。
5|NotExists|0|10|1||00|intkey=r[1]; pk
P3是rowid,查找表,判断是否有rowid记录,如果没有,则跳转10
6|Copy|1|2|0||00|r[2]=r[1]
7|Column|0|1|3||00|r[3]=t1.user_id
读取第1列到寄存器r[3]
8|Column|0|3|4||00|r[4]=t1.c2
读取第3列到寄存器r[4]
9|ResultRow|2|3|0||00|output=r[2..4]
生成结果集
10|Close|0|0|0||00|
11|Halt|0|0|0||00|
关闭所有打开的游标,P1是返回给用户的错误码,根据P2值确定是否需要rollback。
12|Transaction|0|0|7|0|01|
13|TableLock|0|5|0|t1|00|iDb=0 root=5 write=0
14|Goto|0|1|0||00|
(3)UPDATE
sqlite> explain update t1 set user_id=888 where rowid=111;
addr|opcode|p1|p2|p3|p4|p5|comment
0|Init|0|16|0||00|Start at 16
1|Null|0|1|2||00|r[1..2]=NULL
2|OpenWrite|0|5|0|4|00|root=5 iDb=0; t1
打开写游标
3|Explain|0|0|0|SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)|00|
4|Integer|111|7|0||00|r[7]=111
5|MustBeInt|7|8|0||00|
6|NotExists|0|8|7||00|intkey=r[7]; pk
查不到则跳转8
7|Rowid|0|2|0||00|r[2]=rowid
8|IsNull|2|15|0||00|if r[2]==NULL goto 15
没有找到,跳转15结束
9|Null|0|3|0||00|r[3]=NULL
10|Integer|888|4|0||00|r[4]=888
11|Column|0|2|5||00|r[5]=t1.c1
12|Column|0|3|6||00|r[6]=t1.c2
13|MakeRecord|3|4|8|DDBB|00|r[8]=mkrec(r[3..6])
新建record
14|Insert|0|8|2|t1|05|intkey=r[2] data=r[8]
同一个rowid,进行覆盖。
15|Halt|0|0|0||00|
16|Transaction|0|1|7|0|01|
开启写事务
17|TableLock|0|5|1|t1|00|iDb=0 root=5 write=1
对表t1上写锁
18|Goto|0|1|0||00|
(4)DELETE
sqlite> explain delete from t1 where rowid=111;
addr|opcode|p1|p2|p3|p4|p5|comment
0|Init|0|11|0||00|Start at 11
1|Null|0|1|0||00|r[1]=NULL
2|OpenWrite|0|5|0|4|00|root=5 iDb=0; t1
3|Explain|0|0|0|SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)|00|
4|Integer|111|2|0||00|r[2]=111
5|MustBeInt|2|8|0||00|
6|NotExists|0|8|2||00|intkey=r[2]; pk
7|Goto|0|9|0||00|
8|Goto|0|10|0||00|
9|Delete|0|1|0|t1|00|
删除游标所指的记录
10|Halt|0|0|0||00|
11|Transaction|0|1|7|0|01|
12|TableLock|0|5|1|t1|00|iDb=0 root=5 write=1
13|Goto|0|1|0||00|
小结
通过上面的INSERT,SELECT,UPDATE,DELETE语句,我简单介绍了语句中包含的指令,以及指令的含义。经过这个过程,相信大家对SQLite执行流程有了更深的认识,也更能理解指令是如何存取数据的。SQLite中总共包含了100多条指令,对于每条指令的详细含义可以参考官方文档:https://www.sqlite.org/opcode.html
SQLite学习笔记(十二)&&虚拟机指令的更多相关文章
- 汇编语言学习笔记(十二)-浮点指令----ACM
http://blog.csdn.net/q_l_s/article/details/54909328
- python3.4学习笔记(十二) python正则表达式的使用,使用pyspider匹配输出带.html结尾的URL
python3.4学习笔记(十二) python正则表达式的使用,使用pyspider匹配输出带.html结尾的URL实战例子:使用pyspider匹配输出带.html结尾的URL:@config(a ...
- Go语言学习笔记十二: 范围(Range)
Go语言学习笔记十二: 范围(Range) rang这个关键字主要用来遍历数组,切片,通道或Map.在数组和切片中返回索引值,在Map中返回key. 这个特别像python的方式.不过写法上比较怪异使 ...
- java jvm学习笔记十二(访问控制器的栈校验机制)
欢迎装载请说明出处:http://blog.csdn.net/yfqnihao 本节源码:http://download.csdn.net/detail/yfqnihao/4863854 这一节,我们 ...
- (C/C++学习笔记) 十二. 指针
十二. 指针 ● 基本概念 位系统下为4字节(8位十六进制数),在64位系统下为8字节(16位十六进制数) 进制表示的, 内存地址不占用内存空间 指针本身是一种数据类型, 它可以指向int, char ...
- Python学习笔记(十二)—Python3中pip包管理工具的安装【转】
本文转载自:https://blog.csdn.net/sinat_14849739/article/details/79101529 版权声明:本文为博主原创文章,未经博主允许不得转载. https ...
- ROS学习笔记十二:使用gazebo在ROS中仿真
想要在ROS系统中对我们的机器人进行仿真,需要使用gazebo. gazebo是一种适用于复杂室内多机器人和室外环境的仿真环境.它能够在三维环境中对多个机器人.传感器及物体进行仿真,产生实际传感器反馈 ...
- JavaBean(web基础学习笔记十二)
一.JavaBean简介 JavaBean是使用Java语言开发的一个可重用的组件,在JSP的开发中可以使用JavaBean减少重复代码,使整个JSP代码的开发更简洁.JSP搭配JavaBean来使用 ...
- SQLite学习笔记(十)&&加密
随着移动互联网的发展,手机使用越来越广泛,sqlite作为手机端存储的一种解决方案,使用也非常普遍.但是sqlite本身安全特性却比较弱,比如不支持用户权限,只要能获取到数据库文件就能进行访问:另外也 ...
随机推荐
- [Intel Edison开发板] 05、Edison开发基于MRAA实现IO控制,特别是UART通信
一.前言 下面是本系列文章的前几篇: [Intel Edison开发板] 01.Edison开发板性能简述 [Intel Edison开发板] 02.Edison开发板入门 [Intel Edison ...
- HTML5网页录音和压缩,边猜边做..(附源码)
宣传一下自己的qq群: (暗号:C#交流) 欢迎喜欢C#,热爱C#,正在学习C#,准备学习C#的朋友来这里互相学习交流,共同进步 群刚建,人不多,但是都是真正热爱C#的 我也是热爱C#的 希望大家可以 ...
- 关于apue.3e中apue.h的使用
关于apue.3e中apue.h的使用 近来要学一遍APUE第三版,并于此开博做为记录. 先下载源文件: # url: http://http//www.apuebook.com/code3e.htm ...
- TODO:Github的使用技巧之同步代码
TODO:Github的使用技巧之同步代码 GitHub 是一个面向开源及私有软件项目的托管平台,因为只支持 Git 作为唯一的版本库格式进行托管,故名 GitHub. GitHub 于 2008 年 ...
- 三、Redis基本操作——List
小喵的唠叨话:前面我们介绍了Redis的string的数据结构的原理和操作.当时我们提到Redis的键值对不仅仅是字符串.而这次我们就要介绍Redis的第二个数据结构了,List(链表).由于List ...
- 08. Web大前端时代之:HTML5+CSS3入门系列~H5 Web存储
Web大前端时代之:HTML5+CSS3入门系列:http://www.cnblogs.com/dunitian/p/5121725.html
- 一个技术汪的开源梦 —— 基于 .Net Core 的公共组件之序列化
一个技术汪的开源梦 —— 目录 想必大家在项目中都接触过 JSON 或者 XML 吧,为了将对象在网络上传输或者将其持久化必须将其序列化为一个字符串然后进行后续操作.常见的就是将其序列化成 JSON ...
- 【NLP】Python NLTK 走进大秦帝国
Python NLTK 走进大秦帝国 作者:白宁超 2016年10月17日18:54:10 摘要:NLTK是由宾夕法尼亚大学计算机和信息科学使用python语言实现的一种自然语言工具包,其收集的大量公 ...
- 使用session页面控制登录入口及购物车效果的实现
由于 Session 是以文本文件形式存储在服务器端的,所以不怕客户端修改 Session 内容.实际上在服务器端的 Session 文件,PHP 自动修改 Session 文件的权限,只 ...
- 你真的会玩SQL吗?无处不在的子查询
你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...