文件布局1SQLite3 文件格式分析闲话编程存储Contents文件布局 ............................................................................................... 1页(page) ............................................................................................ 2页大小()文件头和的文件布局变长整数单元格()的格式记录格式 ............................................................................................... 7样例分析 ............................................................................................... 7内置表最近一直在思考如何使用文件结构做一个单文件的加密存储格式,因此研究了一下的文件格式以为参考。文件布局每一个的数据库文件由个或者多个大小相同的页()构成,其文件布局如下:其中,第一个记为(而不是从开始算)。任一个都属于以下的某一种:,设计给使用,本身并不需要,任何大小在以内的数据库文件都不包含。,数据库的空闲(譬如删除一些数据之后,页仍然保留),存储一个位的数组,因最小的可用空间为所以至少可以存储个。其中第一个为下一个的,如果没有则为空,不包含任何信息 页(page)2页(page)页大小(page_size)页的大小必须为间的的整数幂。从开始,默认的大小从调整到了。可以通过如下的命令来查看当前数据库的页大小:sqlite> pragma page_size;4096sqlite> pragma page_count;3可以通过命令来设置,不过必须在创建库之前操作,否则不能生效。hfli@192:btree/sqlite $ sqlite3 p512.dbSQLite version 3.28.0 2019-04-16 19:49:53Enter ".help" for usage hints.sqlite> pragma page_size;4096sqlite> pragma main.page_size=512;sqlite> pragma page_size;512然后我们创建一个简单的表,来一探数据库文件的究竟:create table person( id integer not null primary key, name text, age number, remark text);创建完成后,不插入数据,则文件中共有三页,53514C69746520666F726D617420330002000101004020200000000100000003...0100000000000000000000000000000000000000000000000000000000000000 ...0D00000000020000000000000000000000000000000000000000000000000000...文件头数据库文件的第一个页为一个特殊的页,其中包含的是数据库的文件头。上述的数据库文件头为:53514C69 74652066 6F726D61 74203300 02000101 00402020 00000001 0000000300000000 00000000 00000001 00000004 00000000 00000003 00000001 0000000000000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001002E3420 0D000000 01017A00 017A0000 00000000 00000000 00000000 00000000 页(page)300000000 00000000 00000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 00000000 00008103 0107171919018161 7461626C 65706572 736F6E70 6572736F 6E034352 45415445 205441424C452070 6572736F 6E280A20 20202069 6420696E 74656765 72206E6F 74206E756C6C2070 72696D61 7279206B 65792C0A 20202020 6E616D65 20746578 742C0A2020202061 6765206E 756D6265 722C0A20 20202072 656D6172 6B207465 78740A29其详细格式如下: 页(page)4样例值同(,如果是则表示为,用于向后兼容。若新版本可被旧版本安全读取但不可写入,则可设置为比就版本的更高。,用于向后兼容,若大于则不允许读取或者写入。每页的保留字节数(在页的末尾),通常为,可用于存储一些额外的信息,例如让存储等。暂时不支持更改文件修改次数数据库页的数目第一个的总共的数目,每当变化的时候这个值就增长(对应到一个,如果变化也必须重新。允许的格式为或者,类似用于向后兼容,以支持新的语法,当前最新为()默认的缓存大小数据库文件编码,,为使用作为应用格式而设计保留字段,填充为 页(page)5B-tree pagetable b-tree 和 index b-tree中通过来持久化的节点,每一个的节点就对应到一个。其中,又分为两种具体的用途:使用位有符号整数作为(也即是,数据保存在叶子节点中(内部节点中只包含和指向子节点的指针),因此来看这是属于结构只存储而没有数据对于一个的内部节点,存储有个和个指向子节点的指针,在中即节点的。一个以及其左边子节点的组合被称之为一个单元格(),而最右侧的指针没有对应的,是单独储存的。每个数据库都有两个特殊的一个用来存储所有的,包含系统的表,这个的即第一个,并存储了其他表和的的序号一个用来存储中的除此之外,普通用户创建的表则对应到一个(有一个例外就是如果建表没有指定则会使用而不是。b-tree page的文件布局在文件中的格式如下:如果是第一个,则有字节的文件头,占个或者个字节数组,假设有个,则存储个字节的,到位置的偏移,按升序排列空闲空间区域保留区域其中,文件头的格式如下::内部页内部页,叶子页,叶子页第一个的的个数区域起始偏移区域中的碎片大小当前节点最右侧的子节点的,只存在于内部节点中中的空闲区域用链表来标记,每个的结构如下:第一个的偏移存储在文件头中,下一个的存储在的前两个字节中,若已经是最后一个则为。 页(page)6接下来两个字节为的(包含上述个字节的头)由此可见至少需要个字节,如果空闲区域长度小于,则被称之为一个碎片(),这些碎片的总计大小存储在的文件头中。在一个格式良好的中,碎片的总大小不应该超过字节。而也会通过重新组织文件来去掉碎片和,这称之为碎片整理()。变长整数variable-length integer为节省空间,中通过来存储霍夫曼编码的补码位整数,占个字节。设其从低到高分别为则其解码如下:若,则若,则若,则若,则若,则若,则若,则若,则若,则单元格(cell)的格式根据类型的不同,的格式也不相同:::总计的大小(包含):(未包含中的字节)字节的指向第一个,如果没有溢出则不计字节的,指向左边的子节点:总计的大小(包含)(未包含中的字节)字节的指向第一个,如果没有溢出则不计字节的,指向左边的子节点:总计的大小(包含)(未包含中的字节)字节的指向第一个,如果没有溢出则不计 样例分析7overflow page对于的叶子节点,其如果超过一个阈值,无法完整存储到单个中,则会使用链表来存储余下的部分。设为没页的可用大小,为的大小,为页中最大直接存储的大小为最小必须存到中的大小,则:记录格式中的(或者中的)都是存储为记录格式。每一个包含文件头和,依如下格式:文件头的长度,包含自身数组,一个或者多个,记录每一列的数据类型其中,如下:位整数(补码)位整数(补码)位整数(补码)位整数(补码)位整数(补码)位整数(补码)位浮点数((保留如果偶数则为,长度为;如果为奇数则为字符串,长度为(结尾符不存储)在某些情况下,值的个数可能少于,例如通过来增加列,并未修改已有数据。这种情况下新增列的值为默认值。样例分析内置表sqlite_schema系统的第一页是内置的表,这个表类似这样:CREATE TABLE sqlite_schema( type text, name text, tbl_name text, rootpage integer, sql text);创建一个新表并插入数据: 样例分析8create table person( id integer not null primary key, name text, age number, remark text);insert into person values(1, 'riguz', 20, 'a programmer');一直向表中插入数据,insert into person values(1, 'riguz1', 20, 'a programmer');insert into person values(2, 'riguz2', 20, 'a programmer');insert into person values(3, 'riguz3', 20, 'a programmer');insert into person values(4, 'riguz4', 20, 'a programmer');insert into person values(5, 'riguz5', 20, 'a programmer');insert into person values(6, 'riguz6', 20, 'a programmer');insert into person values(7, 'riguz7', 20, 'a programmer');insert into person values(8, 'riguz8', 20, 'a programmer');insert into person values(9, 'riguz9', 20, 'a programmer');insert into person values(10, 'riguz10', 20, 'a programmer');insert into person values(11, 'riguz11', 20, 'a programmer');insert into person values(12, 'riguz12', 20, 'a programmer');insert into person values(13, 'riguz13', 20, 'a programmer');insert into person values(14, 'riguz14', 20, 'a programmer');insert into person values(15, 'riguz15', 20, 'a programmer');insert into person values(16, 'riguz16', 20, 'a programmer');insert into person values(17, 'riguz17', 20, 'a programmer');insert into person values(18, 'riguz18', 20, 'a programmer');当插入到第条数据的时候,了节点,如图: 样例分析9