• SQLite入门与分析(八)存储模型(3)


    写在前面:接上一节,本节主要讨论索引页面格式,以及索引与查询优化的关系。

    (1)索引页面格式
    sqlite> select * from sqlite_master;
    table|episodes|episodes|2|CREATE TABLE episodes( id integer primary key,name tex
    t, cid int)
    index|name_index|episodes|3|CREATE INDEX name_index on episodes(name)
    第3个页面保存表 episodes的索引(也只占一个页面)。

    前8个字节为页面头:
    0x0A:leaf+zerodata,表示叶子页面,且页面中只有关键字,没有数据(即索引页面);
    0x0000:表示第一个空闲块的偏移为0;
    0x0011:页面的单元数(记录数),该页面含有17个记录;
    0x030D:单元内容区的第一个字节的偏移(距页面起始位置)
    0x00:  碎片字节数
    接下来34个字节为17个单元(记录)的指针数组。第一个单元的偏移为0x03EC,如

    来看看索引单元的格式:
    0x13:数据的字节数,19个字节,从0x03开始。
    0x03:记录头的字节数。
    0x2B:第一个字段的长度,15个字节,该索引是对episodes表的name字段建的,其值为episodes表name字段的值。
    0x01:第二个字段的长度,其值为0x01,即episodes表中的对应记录rowid的值。

    (2)索引与order by
    order by是查询中经常用到的,一些通用DBMS(比如DM,Mysql)都提供基于索引的形式来实现Order by。SQLite也是通过索引来实现Order by的。当字段有索引时,则直接通过索引很容易实现排序;另一方面,如果排序的字段没有索引,则以该字段为索引(这种情况下是聚集索引)建立一张临时表,再将临时表按顺序输出。来看看sqlite的实现吧。

    在sqlite中,默认以rowid来建立聚集索引(对于没有整型值主键的情况)。如果主键字段为整型,则将其直接保存在rowid中,实现聚集索引;另一方面,如果主键是字符串,则对主键建立二级索引。非主键的索引都属于二级索引。
    先来看看以整型ID为主键的情况:
    ///////////////以ID(rowid)为索引(即聚集索引)
    sqlite> explain select * from episodes order by id;
    0|Trace|0|0|0|explain select * from episodes order by id;|00|
    1|Noop|0|0|0||00|
    2|Goto|0|13|0||00|
    3|SetNumColumns|0|3|0||00|
    4|OpenRead|0|2|0||00|        ;打开表episodes,p2(=2)为其根页面
    5|Rewind|0|11|0||00|         ;游标指向第一条记录
    6|Rowid|0|1|0||00|           ;取出记录的rowid
    7|Column|0|1|2||00|          ;取出第1列的值
    8|Column|0|2|3||00|          ;取出第2列的值
    9|ResultRow|1|3|0||00|       ;生成记录结果
    10|Next|0|6|0||01|           ;取下一条记录
    11|Close|0|0|0||00|
    12|Halt|0|0|0||00|
    13|Transaction|0|0|0||00|
    14|VerifyCookie|0|2|0||00|
    15|TableLock|0|2|0|episodes|00|
    16|Goto|0|3|0||00|

    属性有索引的情况:
    /////////////排序的实现——有索引
    //算法思想:
    //(1)从索引中依次读取记录(索引记录的形式如:原索引属性-rowid的键值),并取出rowid.
    //(2)根据(1)中取出的rowid,在原表中查找记录,并生成记录结果.
    sqlite> explain select * from episodes order by name;
    0|Trace|0|0|0|explain select * from episodes order by name;|00|
    1|Noop|0|0|0||00|
    2|Goto|0|18|0||00|
    3|SetNumColumns|0|3|0||00|
    4|OpenRead|0|2|0||00|                  ;打开表,p1为表游标(0),p2为表根页面
    5|SetNumColumns|0|2|0||00|
    6|OpenRead|2|3|0|keyinfo(1,BINARY)|00| ;打开索引,p1为索引游标,p2为根页面
    7|Rewind|2|15|1|0|00|
    8|IdxRowid|2|1|0||00|                  ;从索引记录中取出rowid
    9|Seek|0|1|0||00|                      ;根据rowid从表中查找记录
    10|IdxRowid|2|2|0||00|
    11|Column|2|0|3||00|
    12|Column|0|2|4||00|
    13|ResultRow|2|3|0||00|
    14|Next|2|8|0||00|
    15|Close|0|0|0||00|
    16|Close|2|0|0||00|
    17|Halt|0|0|0||00|
    18|Transaction|0|0|0||00|
    19|VerifyCookie|0|2|0||00|
    20|TableLock|0|2|0|episodes|00|
    21|Goto|0|3|0||00|

    对于没有索引的属性排序:
    ////////////////排序的实现——没有索引
    //(
    1)按查询属性为聚集索引建立一个临时表;
    //(2)按索引顺序输出结果。
    sqlite> explain select * from episodes order by cid
    ;
    0|Trace|0|0|0|explain select * from episodes order by cid;|00|
    1|OpenEphemeral|1|3|0|keyinfo(1,BINARY)|00;p1为临时表游标,p2为临时表列数
    2|Goto|0|30|0||00|
    3|SetNumColumns|0|3|0||00|
    4|OpenRead|0|2|0||00|       ;打开表episodes
    5|Rewind|0|16|0||00|        ;游标移到表的第1条记录,p1为游标下标
    6|Rowid|0|1|0||00|              ;p1为表的下标,p2指向表的记录
    7|Column|0|1|2||00|         ;读取表p1(=0)的第1列
    8|Column|0|2|3||00|         ;读取表p1(=0)的第2列
    9|MakeRecord|1|3|4||00|
    10|SCopy|3|5|0||00|
    11|Sequence|1|6|0||00|
    12|Move|4|7|1||00|
    13|MakeRecord|5|3|8||00|
    14|IdxInsert|1|8|0||00|     ;该指令在索引中插入记录,相当于对表的Insert. p1为索引下标,即OpenEphemeral打开的临时表
    15|Next|0|6|0||01|
    16|Close|0|0|0||00|         ;关闭表episodes
    17|SetNumColumns|0|3|0||00|
    18|OpenPseudo|2|1|0||00|    ;打开临时表
    19|Sort|1|28|0||00|         ;与Rewind功能类似
    20|Column|1|2|4||00|
    21|Integer|1|8|0||00|
    22|Insert|2|4|8||00|
    23|Column|2|0|1||00|
    24|Column|2|1|2||00|
    25|Column|2|2|3||00|
    26|ResultRow|1|3|0||00|     ;输出临时记录
    27|Next|1|20|0||00|
    28|Close|2|0|0||00|         ;
    29|Halt|0|0|0||00|
    30|Transaction|0|0|0||00|
    31|VerifyCookie|0|2|0||00|
    32|TableLock|0|2|0|episodes|00|
    33|Goto|0|3|0||00|

  • 相关阅读:
    JS定时器做物体运动
    JS做动态表格
    JS如何做2048(详细)
    改变 C/C++ 控制台程序的输出颜色和样式
    The Game Of Life – 数据结构与算法的敲门砖
    适用于 macOS 下 2K 显示器开启 HiDPI 的简便解决方案
    「踩坑记」Android API 判断权限申请结果的闪退问题
    Hello World!
    js 放大镜效果
    js 随机验证码生成及校验
  • 原文地址:https://www.cnblogs.com/hustcat/p/1568060.html
Copyright © 2020-2023  润新知