下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

SQLite剖析之存储模型

作者:课课家教育     来源: http://www.kokojia.com点击数:930发布时间: 2017-08-23 08:30:32

标签: 数据库SQLite数据库模型

  SQLite作为嵌入式数据库,通常针对的应用的数据量相对于通常DBMS的数据量是较小的。所以它的存储模型设计得非常简单,总的来说,SQLite把一个数据文件分成若干大小相等的页面,然后以B树的形式来组织这些页面。而对于大型的数据库管理系统,比如Oracle,或者DM,存储模型要复杂得多。就拿Oracle来说吧,它对数据文件不仅从物理进行分块,而且从逻辑上进行分段,盘区和页的一个层次划分,DM也一样。不管怎么说,数据库文件要存储大量的数据,为了更好管理,查询和操作数据文件,DBMS不得不从物理上、逻辑上对数据文件的数据进行复杂的组织。

  1、文件格式

  1.1、数据库名称

  应用程序通过sqlite3_openAPI来打开数据库,该函数的一个参数为数据库文件的名称。SQLite内部命名为main数据库(除了临时数据库和内存数据库)。SQLite对每一个数据库都创建一个独立的文件。

  在SQLite内部,数据文件名不是数据库名。SQLite对应用程序的每一个连接都维护着一个单独的临时数据库(temp数据库),临时数据库存临时对象,例如:表以及相应的索引。这些临时对象仅仅对同一个连接可见(对同一个线程,进程的其它连接是不可见的),SQLite存储临时数据库到一个单独的临时文件中,当应用程序关闭对main数据库的连接时,就删除临时文件。

  1.2、数据库文件结构

  除了内存数据库,SQLite把一个数据库(main和temp)都存储到一个单独的文件。

  1.2.1、页面(page)

  为了更好的管理和读/写数据库,SQLite把一个数据库(包括内存数据库)分成一个个固定大小的页面。页面大小的范围从512-32768(两者都包含),页面默认大小为1024个字节(1KB),实际上,页面的上限由2个字节的有符号整数决定。整个数据库可以看成这些页面的数组,页面数组的下标为页面的编号(pagenumber),pagenumber从1开始,一直到2,147,483,647(2^31–1)。实际上,数组上界还受文件系统允许的最大文件大小决定。0号页面视为空页面(NULLpage),物理上不存在,1号页面从文件的0偏移处开始,一个页面接着下一个页面。

  注:一旦数据库创建,SQLite使用编译时确定的默认的页面大小。当然,在创建第一个表之前,可以通过pragma命令改变页面大小。SQLite把该值作为元数据的一部分存储在文件中。

  1.2.2、页面类型

  页面(page)分四种类型:叶子页面(leaf),内部页面(internal),溢出页面(overflow)和空闲页面(free)。内部页面包含查询时的导航信息,叶子页面存储数据,例如元组。如果一个元组的数据太大,一个页面容纳不下,则一些数据存储在B树的页面中,余下的存储在溢出页面中。

  1.2.3、文件头(fileheader)

  作为文件开始的1号页面比较特殊,它包括100个字节的文件头。当SQLite创建文件时例初始化文件头,文件头的格式如下:

  作为文件开始的1号页面比较特殊,它包括100个字节的文件头。当SQLite创建文件时例初始化文件头,文件头的格式如下:

  示例数据(100个字节):

示例数据(100个字节):

  前16个字节-Headerstring(头字符串):"SQLiteformat3."

  0x0400:页面大小-Pagesize,即1024

  0x0101:文件格式-Fileformat(写、读各一字节),在当前的版本都为1。

  0x00:保留空间-Reservedspace,1个字节,SQLite在每个页面的末尾都会保留一定的空间,留作它用,默认为0。

  0x402020:maxembeddedpayloadfraction(偏移21)的值限定了B树内节点(页面)中一个元组(记录,单元)最多能够使用的空间,255意味着100%,默认值为0x40,即64(25%),这保证了一个结点(页面)至少有4个单元。如果一个单元的负载(payload,即数据量)超过最大值,则溢出的数据保存到溢出的页面,一旦SQLite分配了一个溢出页面,它会尽可能多的移动数据到溢出页面;下限为minembeddedpayloadfractionvalue(偏移为22),默认的值为32,即12.5%;minleafpayloadfraction的含义与minembeddedpayloadfraction类似,只不过是它是针对B树的叶子结点,默认值为32,即12.5%,叶子结点最大的负载为通常是100%,这不用保存。

  0x00000011:文件修改计数-Filechangecounter,通常被事务使用,它由事务增加其值。该值的主要目的是数据库改变时,pager避免对缓存进行刷盘。

  空闲页面链表(Freelist):在文件头偏移32的4个字节记录着空闲页面链的第一个页面。

  空闲页面的数量:偏移36处的4个字节为空闲页面的数量。

  空闲页面链表的组织形式如下:

空闲页面的数量:偏移36处的4个字节为空闲页面的数量。    空闲页面链表的组织形式如下

  空闲页面分为两种页面:trunkpages(主页面)和leafpages(叶子页面)。文件头的指针指向空闲链表的第一个trunkpage,每个trunkpage指向多个叶子页面。

  Trunkpage的格式如下,从页面的起始处开始:

  (1)4个字节,指向下一个trunkpage的页面号;

  (2)4个字节,该页面的叶子页面指针的数量;

  (3)指向叶子页面的页面号,每项4个字节。

  当一个页面不再使用时,SQLite把它加入空闲页面链表,并不从本地文件系统中释放掉。当添加新的数据到数据库时,SQLite就从空闲链表上取出空闲页面用来再存储数据。当空闲链表为空时,SQLite就通过本地文件系统增加新的页面,添加到数据库文件的末尾。

  注:可以通过vacuum命令删除空闲链表,该命令通过把数据库中数据拷贝到临时文件,然后在事务的保护下,用临时文件中的复本覆盖原数据库文件。

  元数据变量(Metavariables):从偏移为40开始,为15个4字节的元数据变量,这些元数据主要与B树和VM有关。如下:

注:可以通过vacuum命令删除空闲链表,该命令通过把数据库中数据拷贝到临时文件,然后在事务的保护下,用临时文件中的复本覆盖原数据库文件。    元数据变量(Metavariables):从偏移为40开始,为15个4字节的元数据变量,这些元数据主要与B树和VM有关。如下:

  1.2.4、读取文件头

  当应用程序调用sqlite3_open(API)打开数据库文件时,SQLite就会读取文件头进行数据库的初始化。

1.2.4、读取文件头    当应用程序调用sqlite3_open(API)打开数据库文件时,SQLite就会读取文件头进行数据库的初始化。

  2.页面结构(pagestructure)

  数据库文件分成固定大小的页面。SQLite通过B+tree模型来管理所有的页面。页面(page)分三种类型:treepage、overflowpage、freepage。

  2.1、Treepagestructure

  每个treepage分成许多单元(cell),一个单元包含一个(或部分)payload。Cell是treepage进行分配或回收的基本单位。

  一个treepage分成四个部分:

 数据库文件分成固定大小的页面。SQLite通过B+tree模型来管理所有的页面。页面(page)分三种类型:treepage、overflowpage、freepage。    2.1、Treepagestructure    每个treepage分成许多单元(cell),一个单元包含一个(或部分)payload。Cell是treepage进行分配或回收的基本单位。    一个treepage分成四个部分:

  (1)Thepageheader

  (2)Thecellcontentarea

  (3)Thecellpointerarray

  (4)Unallocatedspace

  Cell指针数组与cellcontent相向增长。一个pageheader仅包含用来管理页面的信息,它通常位于页面的开始处(但是对于数据库文件的第一个页面,它开始于第100个字节处,前100个字节包含文件头信息(fileheader))。

  Structureoftreepageheader:

 Cell指针数组与cellcontent相向增长。一个pageheader仅包含用来管理页面的信息,它通常位于页面的开始处(但是对于数据库文件的第一个页面,它开始于第100个字节处,前100个字节包含文件头信息(fileheader))。    Structureoftreepageheader:

  Flag定义页面的格式:如果leaf位被设置,则该页面是一个叶子节点,没有孩子;如果zerodata位被设置,则该页面只有关键字,而没有数据;如果intkey位设置,则关键字是整型;如果leafdata位设置,则tree只存储数据在叶子节点。另外,对于内部页面(internalpage),header在第8个字节处包含指向最右边子节点的指针。

  Cell位于页面的高端,而cell指针数组位于页面的pageheader之后,cell指针数组包含0个或者多个的指针。每个指针占2个字节,表示在cellcontent区域的cell距页面开始处的偏移。页面Cell单元的数量位于偏移3处。

  由于随机的插入和删除单元,将会导致一个页面上Cell和空闲区域互相交错。Cell内容区域(cellcontentarea)中没有使用的空间收集起来形成一个空闲块链表,这些空闲块按照它们地址的升序排列。页面头1偏移处的2个字节指向空闲块链表的头。每一个空闲块至少4个字节,每个空闲块的开始4个字节存储控制信息:头2个字节指向下一个空闲块(0意味着没有下一个空闲块了),剩余的2个字节为该空闲块的大小。由于空闲块至少为4个字节大小,所以单元内容空间中的3个字节或更小的空间(叫做fragment)不能存在于空闲块列表中。所有碎片(fragment)的总的字节数将记录在页面头偏移为7的位置(所以太碎片最多为255个字节,在它达到最大值之前,页面会被整理)。单元内容区域的第一个字节记录在页面头偏移为5的地方。这个值为单元内容区域和未使用区域的分界线。

  2.2、单元格式(Structureofacell)

  单元是变长的字节串。一个单元存储一个负载(payload),它的结构如下:

2.2、单元格式(Structureofacell)    单元是变长的字节串。一个单元存储一个负载(payload),它的结构如下:

  对于内部页面,每个单元包含4个字节的左孩子页面指针;对于叶子页面,单元不需要孩子指针。接下来是数据的字节数,和关键字的长度,下图描述了单元格式:(a)一个单元的格式(b)负载的结构。

 对于内部页面,每个单元包含4个字节的左孩子页面指针;对于叶子页面,单元不需要孩子指针。接下来是数据的字节数,和关键字的长度,下图描述了单元格式:(a)一个单元的格式(b)负载的结构。

  2.3、溢出页面

  小的元组能够存储在一个页面中,但是一个大的元组可能要扩展到溢出页面,一个单元的溢出页面形成一个单独的链表。每一个溢出页面(除了最后一个页面)全部填充数据(除了最开始处的4个字节),开始处的4个字节存储下一个溢出页面的页面号。最后一个页面甚至可以只有一个字节的数据,但是一个溢出页面绝不会存储两个单元的数据。

  溢出页面的格式:

2.3、溢出页面    小的元组能够存储在一个页面中,但是一个大的元组可能要扩展到溢出页面,一个单元的溢出页面形成一个单独的链表。每一个溢出页面(除了最后一个页面)全部填充数据(除了最开始处的4个字节),开始处的4个字节存储下一个溢出页面的页面号。最后一个页面甚至可以只有一个字节的数据,但是一个溢出页面绝不会存储两个单元的数据。    溢出页面的格式:

  小编结语:

  更多内容尽在课课家教育!

赞(29)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程