本篇介绍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的存储性能进行一定的分析,比如数据库页面的大小对增删改查的影响。

转载于:https://www.cnblogs.com/bbqzsl/p/5943586.html

sqlite3存储格式相关推荐

  1. 如何在sqlite3连接中创建并调用自定义函数

    #!/user/bin/env python # @Time :2018/6/8 14:44 # @Author :PGIDYSQ #@File :CreateFunTest.py '''如何在sql ...

  2. Sqlite3支持的数据类型 日期函数 Sqlite3 函数

    Sqlite3支持的数据类型 NULL INTEGER REAL TEXT BLOB 但实际上,sqlite3也接受如下的数据类型: smallint 16 位元的整数. interger 32 位元 ...

  3. linux qt 连接sqlite3,RedHat 9 Linux下在QT3.1中连接SQLite3全过程详细记录

    作者:zieckey([email]zieckey@yahoo.com.cn[/email]) All Rights Reserved 下文介绍的内容都是基于 Linux RedHat 9.0 平台的 ...

  4. 在CentOS 7.5上升级SQLite3过程实录

    业务起因 我在CentOS 7.5上安装python3.7+Django 3.1.7并创建项目时,发现启动demo例子出错了,出错信息如下: django.core.exceptions.Improp ...

  5. SQLite3简单C++包装类源码示例

    一个比较好的SQLite3 C++ wrapper包装类的通常思路是这样的: 数据库连接类,包含连接池,和sqlite3*,负责与数据库文件的连接问题: 一些create table,insert,u ...

  6. 【数据库】sqlite3数据库备份、导出方法汇总

    [数据库]sqlite3常用命令及SQL语句 目录 1.直接拷贝数据库 2.使用.backup .clone 1)交互式 2)脚本 3.导出到csv文件中(其它格式类似) 1)交互式 2)脚本 3)导 ...

  7. 【数据库】sqlite3常用命令及SQL语句

    [数据库]sqlite3数据库备份.导出方法汇总 一.准备工作 0.安装SQLite3 1)ubuntu命令安装(不是最新版本) sudo apt install sqlite3 2)源码安装(可以安 ...

  8. 【iOS】sqlite3的使用(増删改查)

    目录: 一.sqlite3常用函数 二.将sqlite3集成到项目,实现増删改查 三.封装DBManager 四.Demo 一.sqlite3常用函数及解释 (1)sqlite3_open: 用来创建 ...

  9. sqlite3数据的使用(xcode 7,ios9)

    由于考虑将来还要开发Android版本app,为了移植方便,所以使用了sqlite3来做数据持久化,到时候把sql语句拷过去还能用. 1. 首先用xcode载入sqlite3类库 选择工程的TARGE ...

最新文章

  1. AS 4.7安装yum
  2. C++知识点43——解引用运算符和箭头运算符的重载及智能指针类的实现
  3. shell怎么把负数变成正数_excel怎么计算平方根-记住简单的收藏复杂的
  4. 写博客必备的复制黏贴
  5. 在钉钉上怎么手写_胖·评测|亲测!磐度A5数字纸笔手写板能适配多少直播平台?...
  6. Netweaver和SAP云平台的quota管理
  7. [Leetcode] Longest Valid Parentheses
  8. 目标追踪-背景差算法
  9. Halcon内参外参畸变矫正
  10. ArrayList源码解读(jdk1.8)
  11. java希尔排序的实例,Java 插入排序之希尔排序的实例
  12. 网站apm测试软件,apm测试(手速apm在线测试)
  13. iptables案例:使用iptables搭建路由器
  14. VGG16和VGG19的理解
  15. 人工智能基础知识入门
  16. pert图java_甘特图和PERT图
  17. JavaScript数组map方法
  18. 26_摘录的一些精彩语句2
  19. 等待是一个过程,每一种坚守都是幸福
  20. 电商核心业务功能测试分析

热门文章

  1. winform 自适应屏幕分辨率具体操作和注意事项
  2. Automatic IE Testing With Python
  3. AutoMake文档
  4. php的cgi的设置,apache中配置php支持模块模式、cgi模式和fastcgi模式
  5. 穿越时空,跟我一起探索云栖数字谷(2021云栖大会免费送票)
  6. 【KubeVela 官方文档翻译】,欢迎大家踊跃参与
  7. 使用 CoreDNS sidecar 来优化 Kubernetes Pod dns 性能
  8. NFS文件锁一致性设计原理解析
  9. 如何解决大规模机器学习的三大痛点?
  10. 行波iq调制器_低功率IQ调制器的基带设计实例—电路精选(1)