小文件分析 - (一)

主要参考:

  • 1、《SQLite数据库文件格式全面分析.doc 》,链接不详,只是能再网上各个文库上找到此文档。
  • 2、官网文档 Database File Format

前提准备

准备好文件

在win10环境下

PS G:\code-2\sqlite3> ./sqlite3 small.db
SQLite version 3.28.0 2019-04-16 19:49:53
Enter ".help" for usage hints.
sqlite> CREATE TABLE department(id int, dept char(30), emp_id int);
sqlite> insert into department values(1, "test", -1);
sqlite> insert into department values(2, "test", 1);
sqlite> .qu

准备一些小工具

从sqlite官网下载的源码中的tool目录中有一些十分有用的小工具,例如showdbshowwal等,它们都是单个源文件组成,结合官网提供的sqlite-amalgamation-xxx.zip提供的sqlite3.hsqlite3.c就可以编译出来。例如showdb是由showdb.c编译出来的:gcc -g -O0 -o showdb showdb.c sqlite3.c,调试和编译优化选项看个人喜欢,可加可不加。

最最基础的概念

首先要明白三点:

  • sqlite3数据库是由单个文件组成的,而这个文件被称为主数据库文件main database file。这个文件又是由多个页page组成,页按顺序编号,从1开始。sqlite上层的b树最小组成单位就是页,至于如何存储,如何查找等我写完就差不多弄清楚了。还有页的分类都是后话,所有底层设计都是为了上层服务的,个人理解的时候要会把握关键的思路,再慢慢理顺细节。
  • 存储在文件中的数据格式全部都是大端格式,为什么是大端呢?不清楚,就像网络字节序也是用大端存储,不过大端存储在调式的时候有个明显的优势就是符合人类阅读习惯。
  • sqlite_schema 是一个特殊的表(别名有很多,例如sqlite_master),它存放着其余表对应所在的根页号,具体定义如下:
CREATE TABLE sqlite_schema(
  type text,
  name text,
  tbl_name text,
  rootpage integer,
  sql text
);

开始分析这个小文件

文件头

主数据库文件的前100个字节即是文件头。格式官网也给出了:

Offset Size Description
0 16 The header string: "SQLite format 3\000"
16 2 The database page size in bytes. Must be a power of two between 512 and 32768 inclusive, or the value 1 representing a page size of 65536.
18 1 File format write version. 1 for legacy; 2 for WAL.
19 1 File format read version. 1 for legacy; 2 for WAL.
20 1 Bytes of unused "reserved" space at the end of each page. Usually 0.
21 1 Maximum embedded payload fraction. Must be 64.
22 1 Minimum embedded payload fraction. Must be 32.
23 1 Leaf payload fraction. Must be 32.
24 4 File change counter.
28 4 Size of the database file in pages. The "in-header database size".
32 4 Page number of the first freelist trunk page.
36 4 Total number of freelist pages.
40 4 The schema cookie.
44 4 The schema format number. Supported schema formats are 1, 2, 3, and 4.
48 4 Default page cache size.
52 4 The page number of the largest root b-tree page when in auto-vacuum or incremental-vacuum modes, or zero otherwise.
56 4 The database text encoding. A value of 1 means UTF-8. A value of 2 means UTF-16le. A value of 3 means UTF-16be.
60 4 The "user version" as read and set by the user_version pragma.
64 4 True (non-zero) for incremental-vacuum mode. False (zero) otherwise.
68 4 The "Application ID" set by PRAGMA application_id.
72 20 Reserved for expansion. Must be zero.
92 4 The version-valid-for number.
96 4 SQLITE_VERSION_NUMBER

对应的小文件分析可以用showdb可知:

PS G:\code-2\sqlite3> ./showdb small.db dbheader
Pagesize: 4096
Available pages: 1..2
 000: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
 010: 10 00 01 01 00 40 20 20 00 00 00 03 00 00 00 02 .....@  ........
 020: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04 ................
 030: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................
 040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
 050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 ................
 060: 00 2e 34 20 00                                  ..4 .
Decoded:
 010: 10 00              4096  Database page size
 012: 01                    1  File format write version
 013: 01                    1  File format read version
 014: 00                    0  Reserved space at end of page
 018: 00 00 00 03           3  File change counter
 01c: 00 00 00 02           2  Size of database in pages
 020: 00 00 00 00           0  Page number of first freelist page
 024: 00 00 00 00           0  Number of freelist pages
 028: 00 00 00 01           1  Schema cookie
 02c: 00 00 00 04           4  Schema format version
 030: 00 00 00 00           0  Default page cache size
 034: 00 00 00 00           0  Largest auto-vac root page
 038: 00 00 00 01           1  Text encoding
 03c: 00 00 00 00           0  User version
 040: 00 00 00 00           0  Incremental-vacuum mode
 044: 00 00 00 00           0  Application ID
 048: 00 00 00 00           0  meta[8]
 04c: 00 00 00 00           0  meta[9]
 050: 00 00 00 00           0  meta[10]
 054: 00 00 00 00           0  meta[11]
 058: 00 00 00 00           0  meta[12]
 05c: 00 00 00 03           3  Change counter for version number
 060: 00 2e 34 20     3028000  SQLite version number

页大小 (Database page size)

在0x10偏移处,规定了页的大小,取值必须是2的幂次方,范围为[512, 65536],这里有点小细节可以参考官网文档可知。

文件格式版本(File format write/read version)

现在仅存在1和2两种,分别代表Hot journalWAL journal。小文件分析时候采用的是前者。

页的保留空间大小 (Reserved space at end of page)

保留空间是在页的尾部开始,其的用途例如在加密插件启用的时候,存储nonce值或者页面的校验和。一般来说这个字段一般为0。

Payload Fraction

0x21-23是连续的页内每个单元占用的百分比,以255为分母,这三个值64\32\32分别做分子,可得到对应的百分比25%,12.5%,12.5%。前两个是给index使用的,分别表示最大和最小值,后一个是给table b-tree leaf page的最小值,其最大值是是pagesize - 35

文件修改次数计数器(File change counter)

统计每一次修改数据库文件,到目前为止,我们先是创建表一次,两次独立(相当于两次事务)的插入操作,所以总共修改了文件3次。

当前数据库页的数目(Size of database in pages)

字面意思,不过历史版本上对其处理还是有不一样的,这个值会需要进一步的和数据库文件大小相比较,如果不一致则采用具体的数据库文件大小。

空闲页

这里开始涉及到页的分类了,先简单的理解一下空闲页。它的存在是因为数据库在进行删除操作的时候,导致整一页的数据都被清除掉了,此时就要把该页添加入空闲页链表。
这个链表在文件中的存储方式,是隐式链表,也就是页头会存放着指向下一个空闲页的下标。而第一个空闲页则由主数据库文件的文件头给出。
回到我们的小文件分析,此时我们的数据库没有多余的页,所以Page number of first freelist page == 0,0可以理解为c语言中的NULL,并且空闲页数目也等于0

Schema cookie

这里的Schema,中文翻译怎么都不太恰当。它是整个数据库所有表的总和,而这cookie则是一个整数,每次Schema发生改动(也就是发生表的增删改操作)就自增。这里一个相关的使用场景就是调用sqlite3_step的时候会去检查schema是否发生变化。

Schema format number

它代表着schema不同历史时期的功能支持程度,可以见官网描述。这里直接摘抄原文,现在默认是用版本4。

    1. Format 1 is understood by all versions of SQLite back to version 3.0.0 (2004-06-18).
    1. Format 2 adds the ability of rows within the same table to have a varying number of columns, in order to support the ALTER TABLE ... ADD COLUMN functionality. Support for reading and writing format 2 was added in SQLite version 3.1.3 on 2005-02-20.
    1. Format 3 adds the ability of extra columns added by ALTER TABLE ... ADD COLUMN to have non-NULL default values. This capability was added in SQLite version 3.1.4 on 2005-03-11.
    1. Format 4 causes SQLite to respect the DESC keyword on index declarations. (The DESC keyword is ignored in indexes for formats 1, 2, and 3.) Format 4 also adds two new boolean record type values (serial types 8 and 9). Support for format 4 was added in SQLite 3.3.0 on 2006-01-10.

Default page cache size

默认的页缓存大小,只是一个建议,实际上由程序自己判断

Incremental vacuum settings

Largest auto-vac root pageIncremental-vacuum mode 都是vacuum相关的设置,前者是auto_vacuum模式使用,后者是incremental_vacuum模式使用。当前者等于0时候,ptrmap类型的页将会被从数据库文件中剔除,并且这两种模式都不会被支持。当前者不为0的时候,它指向的是最大的根页,储存着许多ptrmap类型的页,并且此时由后者控制是auto_vacuum还是incremental_vacuum

文本编码 Text encoding

  • 1 for UTF-8
  • 2 for UTF16LE
  • 3 for UTF16BE

User version

这是给用户使用的版本号,sqlite3 并不使用它。这好像在系统升级的时候确实有用

Application ID

这个也是留给用户使用的,表示当前是由那个进程在使用。这个用处没这么大。

sqlite3 库使用的版本

Change counter for version numberSQLite version number,在0x5c和0x60存放的是sqlite3.so/sqlite3.dll在使用此数据库文件时候,存放的版本号,并且自增修改次数。用途不太明确,但是应该用来判断是否有不同版本的sqlite3库修改此文件吧。

你可能感兴趣的:(小文件分析 - (一))