小文件分析 - (一)

主要参考:

  • 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库修改此文件吧。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,135评论 6 514
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,317评论 3 397
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,596评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,481评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,492评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,153评论 1 309
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,737评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,657评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,193评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,276评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,420评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,093评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,783评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,262评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,390评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,787评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,427评论 2 359

推荐阅读更多精彩内容

  • 页 主数据库文件头之后的都是页的内容了,页的分类主要有5种:b-tree 页,overflow 页,free 页,...
    5dplay阅读 278评论 0 0
  • 随着app信息量越来越大,每次从网络获取数据已经不是很可取的方案了,本地数据库的运用已经越来越普遍了。而说道移动端...
    taosiyu阅读 4,655评论 12 13
  • 原文PRAGMA语句是SQLITE数据的SQL扩展,是它独有的特性,主要用于修改SQLITE库或者内数据查询的操作...
    _小沫阅读 1,382评论 0 1
  • SQLite 的 PRAGMA 命令是一个特殊的命令,可以用在 SQLite 环境内控制各种环境变量和状态标志。一...
    元茜姑娘阅读 939评论 0 7
  • WCDB简介 WCDB(wechat dataBase)是一个高效、完整、易用的开源移动数据库框架,基于SQLCi...
    Shmily鱼阅读 13,429评论 0 2