sqlite3存储格式
本篇介绍sqlite3数据库文件的存储格式。
通过阅读源读源代码可以知道sqlite的设计思想。
一个sqlite数据库文件对应着一个数据库。sqlite将数据库文件划分大小一致的存储(以区分内存)页面,并通过一系列数据结构将它们组织起来。
sqlite组织页面的数据结构主要有B树和二维链表。每一个页面要么是B树的叶子或结点,要么是二维链表的一个节点。用作B树的页面都有8或12字节的页面信息头,特别地第一个页面除了是表的根结点是数据库文件的根,所以它首先包含100字节的是库信息,然后才是作为一个是B树的根结点。另一方面闲置的页面没有页面头,它的链头是在第一个页面的库信息头。二维链表的第一维由单向链表实现,第二维由数组实现。用作第一维节点的页面也是一个空闲页面,被统计在库信息头。
数据库层面的表和索引,在底层存储格式中用B树结构组织,第一张表或索引都对应着一棵B树。底层存储中所有空闲(闲置)页面,都由二维链表链接起来。
B树页面,有统一的存储格式。首先是页面信息头,然后紧跟着页内数据排序索引(请区分数据库的表索引,这里是数据结构中的外部排序索引)数组;而页内数据存储在页面的尾部。数据索引和数据单元分别从正反两个方向向页面中间伸展,页面中间部分形式空闲区域。
sqlite数据库文件使用自然字节序存储数据,这种字节序有利于它里面使用到可变长整形解释。
各种具体的数据结构和详细的格式编排请自行参阅源代码,不一一贴上划水。自行用任意一门编程语言写程序遍历解读sqlite数据库文件其中的内容。
在这里定义本篇使用的一些术语:
页面,数据库文件页面,请区分内存页面或虚地址空间页面,尽管sqlite默认页面大小也是4KB。
指针,请区分c语言指针,这里指文件内地址索引,或页面内地址偏移。
单元,cell,存储在B树页面的单位数据。
整型主键,sqlite表默认的自增计数。
变长整型,sqlite存储中使用到的一种基于自然字节序的变长整数存储方式。每字节低7位为有效数值位,高位标记下一字节是否继续为当前变长整数的一部分,高位为0表示为变长整数的最后一个字节。
叶子,B树的叶子结点。
结点,B树的非终端结点。
索引,当提及结构化查询时指索引表,而当提及低层存储格式时等同于编号,地址和指针。
前面总括了底层存储组织,现在结合上一层数据库层来看存储组织,库和表的存储。sqlite将一切表和索引视作表,并以B树的形式保存在数据库文件。sqlite数据库默认有一张管理表,名为sqlite_master。这张表记录着用户创建的所有表和索引的名字,左联合表名,表的根页面索引号以及创建sql语句。也就是数据库文件的第一页面一棵入口B树的根,通过这棵树可以索引到其它B树(数据库层面表或索引)的根。当sqlite打开某一张表时,必须遍历管理表找出与表名匹配的记录,从而找出记录中根页面索引号,才能定位到表的入口位置。索引和表是分开存储的,表默认使用是自增计数,因为存储在B树中,所以每条记录都必须有一个唯一键。索引就是一张索引键与自增计数的映射表。当使用索引查询记录时,就相当于索引左联合到数据记录的表,条件为自增计数。总结来说,就是sqlite数据库文件里保存了多于一棵的B树,第一棵B树入口位于第一页面,其它B树的根必须通过第一棵B树才能索引出来。
首先我们来看sqlite数据库的总起信息,也就是入口,位于第一页面的开头100字节。
<img dbheader/>
可看到数据库文件开头作了文件类型标记“SQLite format 3\0”。当前数据库以4KB尺寸来划分页面。数据库文件迄今被修改过461次,并且没有空闲页面。当前默认编码方式为UTF-8。
接着就是第一个页面作为B树根的页面信息。
页面信息首先定义了当前页面的类型,包含自增计数的非叶子结点并且有左孩子记录。跟着就是当前页面的状态信息,有1条记录(单元),下一条插入的记录(单元)的位置,结点的右孩子位于索引号为141的页面(也就是第141个页面)。当“number of cells on this page”和“first byte of the cell content area”越靠近,表示页面内空闲空间越小。
下面是对第一个页面的入口B树跟踪。
第一行是当前页面号为1, 类型是结点,右孩子在141号页面,左孩子分别是139号页面以23为key,只有一个左孩子。
第二行是当前页面号为139,类型是叶子,存放23条记录。根据上下文知,是1号页面的左孩子,并且存放小于等于key23的记录,一共有23条记录。
第三行是当前页面号为141,类型是叶子,存放23条记录。根据上下文知,是1号页面的右孩子,是刚刚分裂成139和141两个叶子,各存放一半数量的记录。
下面是第139号页面,作为左孩子叶子页面的跟踪。
绿色框框是我们关心的信息,描述了记录在当前页面的指针,记录长度以及唯一键值(自增计数)。根据上下文139号页面,这个B树叶子存放自增计数小于等于23的记录一共23条。
蓝色框框是记录单元里的数据,是表的根页面指针,在当前情景中,2,4,5,11和12都是某张表的根页面索引号。
下面我们对2号页面为根的表进行跟踪。
分别对2,4以及12号页面为根三张表的B树存储组织,蓝色框框叶子记录总数与使用sqlite查询的结果一致。
最后我们跟踪一下空闲页面,在本篇中使用的数据库不包含空闲页面,现在我们truncate一个表以产生一些空闲页面,就拿上面举例的12号页面为根的表,这张表包含了1个根结点和6个叶子,其中5个叶子是左孩子,1个叶子是右孩子。
留意绿色框框,文件修改次数被加1,刚刚有一个表被清空了。
蓝色框框显示了本次清空动作,产生了8个空闲页面,而链接这些页面的第一节点在第60号页面。
红色框框显示了放在60号页面节点的页面索引号,对照上面12号页面为根的表跟踪,可以看到被清空的表的存储B树所使用到的页面都被链接到空闲链表。
介绍完以页面为单位存储大框架后,我们再来看页面内的存储。
页面分为三部分,页面信息头,单元(cell)指针(索引)数组,以及数据(记录)单元。索引和数据分别放于页面的一头一尾,中间为未使用空间,各自向中间获取分配空间来应对增长。当某一单元被删除,它的空间会被回收链入到页面内空闲块链表。当空闲块再次被使用时可能规格不一致,从而会产生碎片(零碎字节),这些碎片将不能被重用,因而必须记录下来,供往后页面零碎程序评级用。我猜过于零碎的页面可能会在合适的时机进行重整。也就是单元区域遍历所有单元,必须依赖页面内索引数组。只有索引数组内索引到的单元才是有效的。对于未曾发生过单元回收重用的页面,单元区域可以直接遍历,但发生过单元回收后则不可,必须依赖页面内索引。
虽然说管理表是以B树存储,但是却不是以表名为键而只是单纯的自增计数,所以当表(包括索引,视图等)的数量多的时候,打开其中之一也没有得益于B树结构。
sqlite数据库文件使用的B树是一种B-树。参考我们曾经的教材严女士的《数据结构》对B-树的定义。
一棵m阶的B-树,或为空树,或为满足下列特性的m叉树:
(1)树中每个结点至多有m棵子树;
注:这是由sqlite数据库页面大小限制,对于Key为变长时,并不是固定m阶,而是一个泛数多阶。
(2)若根结点不是叶子,则至少有两棵子树;
注:当表的首个页面空间可以容纳所有数据时为叶子,当不满足时将分裂出两个叶子,开始变成根结点。
(3)除根之外的所有非终端结点至少有m/2的上限棵子树;
注:当结点容量不足时结点会分裂出两个结点,各迁移一半key。
(4)所有的非终端结点中包售下列信息数据(n,(A1,K1),(A2,K2),...(An,Kn), An+1),其中n为K(ey)的个数,A(ddr)是子树地址。
注:n个Key意味有n个左孩子,A1至An是左孩子页面号,An+1是右孩子页面号。n和An+1保存在页面头,(Ax,Kx)|x<n则是结点页面的数据单元。
(5)所有的叶子都出现在同一层次上,并且不带信息。
注:不带信息也就是页面类型是叶子时,数据单元忽略左孩子域。但sqlite有没有维护树高度未能证明。
通过本篇,相信大家已经对sqlite数据库有了很具体的了解了。有了上面的知识大家就能对sqlite的存储性能进行一定的分析,比如数据库页面的大小对增删改查的影响。
sqlite3存储格式的更多相关文章
- iOS 数据存储之SQLite3的使用
SQLite3是iOS内嵌的数据库,SQLite3在存储和检索大量数据方面非常有效,它使得不必将每个对象都加到内存中.还能够对数据进行负责的聚合,与使用对象执行这些操作相比,获得结果的速度更快. SQ ...
- Android中如何使用命令行查看内嵌数据库SQLite3
转载博客:http://www.linuxidc.com/Linux/2011-06/37135.htm 在上图中,除了最后一个红色的方框,其它方框都是adb shell下的命令. [1]在Andro ...
- flask+sqlite3+echarts2+ajax数据可视化
前提: 准备Python + Flask+Sqlite3的平台环境(windows系统) 前面一节介绍flask怎么安装了,剩下sqlite3下载后解压,然后环境变量添加解压路径就行了 附加下载地址: ...
- flask+sqlite3+echarts2+ajax数据可视化报错:UnicodeDecodeError: 'utf8' codec can't decode byte解决方法
flask+sqlite3+echarts2+ajax数据可视化报错: UnicodeDecodeError: 'utf8' codec can't decode byte 解决方法: 将 py文件和 ...
- 安卓使用SQlite3数据库无法id主键无法自动增加?不是的。
安卓使用SQlite3数据库无法id主键无法自动增加?不是的. 要这样写:id integer primary key ,要写integer而不是int所以会报错! http://blog.csdn. ...
- 安卓虚拟机adb shell sqlite3数据库
adb shell 连接: //http://www.cnblogs.com/xiaobo-Linux/ Android把数据都存放在data/data目录下. 我们使用cd命令转到data/data ...
- SQLite源程序分析之sqlite3.c
/****************************************************************************** ** This file is an a ...
- SQLite3源程序分析之查询处理及优化
前言 查询处理及优化是关系数据库得以流行的根本原因,也是关系数据库系统最核心的技术之一.SQLite的查询处理模块很精致,而且很容易移植到不支持SQL的存储引擎(Berkeley DB最新的版本已经将 ...
- SQLite3源程序分析之虚拟机
前言 最早的虚拟机可追溯到IBM的VM/370,到上个世纪90年代,在计算机程序设计语言领域又出现一件革命性的事情——Java语言的出现,它与c++最大的不同在于它必须在Java虚拟机上运行.Java ...
随机推荐
- CCNA实验4:HDLC和PPP
一.HDLC封装 router9和11上分别配置s0/0如下 conf t int s0/0 encapsulation hdlc do show int s0/0 ip address x.x.x. ...
- [Aaronyang] 写给自己的WPF4.5 笔记21 [3d课 2/4]
1. 当然复杂的3d模型我们是可以通过更专业的工具做出来,然后导入项目中,我们只是方便演示,选择简单的图形. Tip: 关于摄像机的NearPlaneDistance和FarPlaneDistance ...
- 多线程socket编程示例
工程: 代码: package com.my.socket.business; /** * 业务实现类 * * @author ZY * */ public class CoreMisBusiness ...
- CSS层叠样式表的层叠是什么意思(转自知乎)
转自知乎上的回答:http://www.zhihu.com/question/20077745 解答一: 层叠指的是样式的优先级,当产生冲突时以优先级高的为准.1. 开发者样式>读者样式> ...
- 文件系统管理 之 Linux 查看磁盘分区、文件系统、使用情况的命令和相关工具介绍
一.df 命令:df 是来自于coreutils 软件包,系统安装时,就自带的:我们通过这个命令可以查看磁盘的使用情况以及文件系统被挂载的位置: 举例: [root@localhost beinan] ...
- java生成解析xml的另外两种方法JAXB
JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术.该过程中,JAXB也提供了将XML实例文档反 ...
- Firefox终于返回到了Debian stable
6月8日,firefox 45.2以安全修复包的名义回到了Debian oldstable (即wheezy),两天以后,Debian 8 jessie里面也有了(https://packages.d ...
- 十分钟理解Gradle
一.什么是Gradle 简单的说,Gradle是一个构建工具,它是用来帮助我们构建app的,构建包括编译.打包等过程.我们可以为Gradle指定构建规则,然后它就会根据我们的“命令”自动为我们构建ap ...
- JVM性能分析与优化
JVM性能分析与优化: http://www.docin.com/p-757199232.html
- 精选19款华丽的HTML5动画和实用案例
下面是本人收集的19款超酷HTML5动画和实用案例,觉得不错,分享给大家. 1.HTML5 Canvas火焰喷射动画效果 还记得以前分享过的一款HTML5烟花动画HTML5 Canvas烟花特效,今天 ...