zoukankan      html  css  js  c++  java
  • 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[4]=rowid
    3|MemMax|2|4|0||00|r[2]=max(r[2],r[4])
    4|SoftNull|5|0|0||00|r[5]=NULL
    5|Integer|1111|6|0||00|r[6]=1111
    6|String8|0|7|0|abc|00|r[7]='abc'
    7|String8|0|8|0|abc|00|r[8]='abc'
    8|MakeRecord|5|4|9|DDBB|00|r[9]=mkrec(r[5..8])
    9|Insert|0|9|4|t1|1b|intkey=r[4] data=r[9]
    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[3]!=NULL goto 14
    13|NewRowid|0|3|0||00|r[3]=rowid
    14|MakeRecord|1|2|10||00|r[10]=mkrec(r[1..2])
    15|Insert|0|10|3||08|intkey=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 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[1]='t1'
    23|Rewind|0|31|0||00|
    24|Column|0|0|2||00|r[2]=
    25|Ne|1|29|2||10|if r[1]!=r[2] goto 29
    26|Rowid|0|3|0||00|r[3]=rowid
    27|Column|0|1|2||00|r[2]=
    28|Goto|0|31|0||00|
    29|Next|0|24|0||00|
    30|Integer|0|2|0||00|r[2]=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 error。

    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; sqlite_sequence

     打开写游标

    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 write=1

    上t1表锁(仅用于shared-cache)

    19

    TableLock|0|3|1|sqlite_sequence|00|iDb=0 root=3 write=1

     上sqlite_sequence表锁

    20

    OpenRead|0|3|0|2|00|root=3 iDb=0; sqlite_sequence

    打开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[2] = NULL
    22 String8 0 1 0 t1 00 r[1]='t1'
    REG[1] = t2[t1](8)
    23 Rewind 0 31 0 00 
    24 Column 0 0 2 00 r[2]=
    REG[2] = s4[user](8)
    25 Ne 1 29 2 10 if r[1]!=r[2] goto 29
    REG[1] = t2[t1](8)
    REG[2] = s4[user](8)
    29 Next 0 24 0 00 
    24 Column 0 0 2 00 r[2]=
    REG[2] = s6[orders](8)
    25 Ne 1 29 2 10 if r[1]!=r[2] goto 29
    REG[1] = t2[t1](8)
    REG[2] = s6[orders](8)
    29 Next 0 24 0 00 
    24 Column 0 0 2 00 r[2]=
    REG[2] = s2[t1](8)
    25 Ne 1 29 2 10 if r[1]!=r[2] goto 29
    REG[1] = t2[t1](8)
    REG[2] = s2[t1](8)
    26 Rowid 0 3 0 00 r[3]=rowid
    REG[3] = i:3
    27 Column 0 1 2 00 r[2]=
    REG[2] = 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[4]=rowid
    REG[2] = i:113
    REG[4] = i:114
    3 MemMax 2 4 0 00 r[2]=max(r[2],r[4])
    REG[4] = i:114
    4 SoftNull 5 0 0 00 r[5]=NULL
    5 Integer 1111 6 0 00 r[6]=1111
    REG[6] = i:1111
    6 String8 0 7 0 abc 00 r[7]='abc'
    REG[7] = t3[abc](8)
    7 String8 0 8 0 abc 00 r[8]='abc'
    REG[8] = t3[abc](8)
    8 MakeRecord 5 4 9 DDBB 00 r[9]=mkrec(r[5..8])
    REG[9] = s13[05000213130457616263616263......Wabcabc](8)
    9 Insert 0 9 4 t1 1B intkey=r[4] data=r[9]
    REG[9] = s13[05000213130457616263616263......Wabcabc](8)
    REG[4] = 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[3]!=NULL goto 14
    REG[3] = i:3
    14 MakeRecord 1 2 10 00 r[10]=mkrec(r[1..2])
    REG[10] = s6[031101743172...t1r](8)
    15 Insert 0 10 3 08 intkey=r[3] data=r[10]
    REG[10] = s6[031101743172...t1r](8)
    REG[3] = 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

  • 相关阅读:
    2星|《自金融》:公开信息汇总,缺乏深度,缺乏自有观点
    3星|《好好工作》:适合职场新手看的工作原则
    QQ2010如何开启透明效果皮肤
    如何制作迅雷7的皮肤,如何获取迅雷7皮肤的图片
    如何使用易我数据恢复向导恢复数码相机删除的图片
    如何使用射手影音寻找字幕
    云端软件平台 如何在云端分享自己的文章
    如何使用千千静听为MP3添加专辑封面和文字信息
    如何使用Ultraiso制作U盘启动盘
    如何使用SubtitleWorkshop制作字幕
  • 原文地址:https://www.cnblogs.com/cchust/p/5143666.html
Copyright © 2011-2022 走看看