Database File Format阅读笔记。

1. The Database File

Sqlite数据库的状态由数据库文件以及日志文件共同构成。日志文件分为两种,一种是活动记录(journal),另一种是WAL格式。

页是SQLite存储的基本单元,页的大小可以从512到65536。页的编号从1开始。页是单用途的,可以作为:

  • lock-byte页
  • freelist枝干,freelist末梢
  • b-tree居间页,b-tree末梢页
  • payload overflow页
  • point map页

SQLite的读写操作都是以整页为单位的,可以覆盖多个整页。一个例外是,数据库的前100字节保存数据库的元信息,所以这100字节不是按页读取的。

1.3. The Database Header列举了数据库的元信息。

1.4. The Lock-Byte Page

Lock-Byte页映射到数据库文件从1073741824 到1073742335区间的字节。这个页面主要是为了应对Win95只支持mandatory file locking而不支持advisory file locking而设立的。现在已经没有参考意义,保留只是为了格式上的向后兼容性。

1.5. The Freelist

Freelist即备用页列表,用来回收暂时不用的页面。freelist分两级,枝干和末梢。枝干以链表方式组织,每个枝干可以拥有若干末梢页。

1.6. B-tree Pages

B-tree中的页具有键名,可以根据键名来索引。SQLite使用了两种B-tree,一种是B*-Tree,所有的数据保存在末梢,主要用来存表数据;另一种是B-Tree,间隔页也用来保存数据,主要用来存索引。

1.7. Cell Payload Overflow Pages

溢出页用来盛放B-tree装不下的内容。

1.8. Pointer Map or Ptrmap Pages

指针映射页面是为了让auto_vacuum或者incremental_vacuum效率更高。

2. Schema Layer

讲述如何使用B-tree来支持SQL。

2.1. Record Format

不管是分页的表数据还是索引数据,都是采用"record format“,指定表的栏目,每个栏目的类型以及每个栏目的内容。record format大量使用了可变长的整型值。

2.2. Record Sort Order

索引的b-tree的顺序是由其中records的键值的顺序决定的。

Record的比对是从左到右按栏进行的。碰到第一个不相同的栏,那么这个栏的比对结果就决定了记录的顺序。单栏的比对规则如下:

  • NULL (serial type 0)排第一
  • 接下来是Numeric数值类型(serial types 1 through 9)
  • 然后是Text也就是文本类型(奇数serial types 13及更大),文本排序受collating function的影响
  • 最后是BLOB类型,混沌类型(偶数serial types 12以及更大)

用于文本的Colatting function有以下几种内建类型:

  • BINARY,使用memcmp来勘比
  • NOCASE,在ASCII范围内不区分大小写
  • RTRIM,跟BINARY类似,不过会把字符串末尾的空白去掉。

使用sqlite3_create_collation() 可以注册自定义的collating模式。默认会采用BINARY模式,可以在CREATE TABLE的时候指定其他模式。除非特别指定,创建索引的时候会创建表的时候采用一样的collating模式。

2.3. Representation Of SQL Tables

Schema里面指定的表在磁盘上以一颗b-tree的方式存储。b-tree中的每一个项目表示一个数据行。rowid是64位有符号整型值。数据存储的时候,所有栏的数据都会合并在一起,然后写入这个record format里面。如果表指定了INTEGER PRIMARY KEY ,那么这一栏为NULL,因为SQLite会使用b-tree的键值。

如果一个数据栏的affinity设为REAL,但是保存的值可以无损转化为整型,那么数据会被保存成整型。

2.4. Representation of WITHOUT ROWID Tables

WITHOUT ROWID的表采用索引b-tree而不是使用表格b-tree。并且会把PRIMARY KEY这一栏(按照在CREATE TABLE中出现的位置)录入键值。

2.5. Representation Of SQL Indices

不论是通过CREATE INDEX生成的索引,还是隐式通过UNIQUE和PRIMARY KEY约束生成的索引,都是采用索引b-tree实现的。

索引B-tree中的每一项对应着所关联的表格B-tree的项。索引B-tree的键值是一个被索引的表栏合并而成的记录,这个记录还包含表行的键值。对于rowid的表,条目的键值是rowid;对于WITHOUT ROWID 的表,条目的键值是PRIMARY KEY。

对于局部索引(partial index),根据其指定的WHERE成分,可以只映射部分关联表格的条目。

略过 2.5.1. Suppression of redundant columns in WITHOUT ROWID secondary indexes

2.6. Storage Of The SQL Database Schema

数据库文件的第一分页是一个表格b-tree根页,记录着一个表,名字叫做sqlite_master(或者sqlite_temp_master,如果这个数据库是TEMP的话),这个表的结构类似于:

CREATE TABLE sqlite_master(
  type text,
  name text,
  tbl_name text,
  rootpage integer,
  sql text
);

sqlite_master的每一行代表一个表、索引、视图或者触发器。sqlite_master不仅包含用户自定义的条目,也包含私有的条目。

sqlite_master.type可以是’table', ‘index’, ‘view’, 或者’trigger'。‘table‘对应的不仅是普通的表格,也可以代表虚拟表格。

sqlite_master.name对应的是条目的名字。如果使用了UNIQUE或者PRIMARY KEY修饰,SQLite会创建内部用的索引,比如"sqlite_autoindex_TABLE_N" 。

对于WITHOUT ROWID 的表格,sqlite_master中没有相应的PRIMARY KEY表现,但是"sqlite_autoindex_TABLE_N" 依然会存在。对于 INTEGER PRIMARY KEY,不会有对应的"sqlite_autoindex_TABLE_N",可能因为采用的是b-tree的节点做键值吧。

sqlite_master.tbl_name栏在索引和触发器中指向原表格。

sqlite_master.rootpage栏保存着表格和索引的根b-tree分页。对于其他类型,这个栏的值为NULL。

sqlite_master.sql保存至相应的归一化的SQL语句。归一化规则:

  • 语句开头的部分会被转化成大写
  • TEMP和TEMPORARY会被移除
  • 数据库名字修饰符会被移除
  • 前导的空白字符会被移除
  • 前面两个关键字之后的空白会被压缩成单个空格

通过UNIQUE和PRIMARY KEY创建的内部索引不会在sqlite_master.sql中体现。

2.6.1. Internal Schema Objects

sqlite_master可能会包含一个内部对象。这些内部对象会以sqlite_开头。内部对象包括:

  • 类似于"sqlite_autoindex_TABLE_N“这种用于实现UNIQUE和PRIMARY KEY约束
  • ”sqlite_sequence“用于跟踪一个表的INTEGER PRIMARY KEY ,如果这个表是AUTOINCREMENT的。
  • ”sqlite_statN“,保存ANALYZE命令生成的统计信息

2.6.2. The sqlite_sequence table

用于实现AUTOINCREMENT。一旦某个表具有这种属性,那么sqlite_sequence就会被生成,而且永远不能丢弃。sqlite_sequence的图样如下:

CREATE TABLE sqlite_sequence(name,seq);

每个条目记录着一个表的AUTOINCREMENT状态。如果AUTOINCREMENT的表满了,则会返回SQLITE_FULL 。

应用程序是可以修改sqlite_sequence的,前提是sqlite_sequence必须存在。应用程序可以删除sqlite_sequence的所有条目,但是不能丢弃这个表。

2.6.3. The sqlite_stat1 table

略。

2.6.4. The sqlite_stat2 table

略。

2.6.5. The sqlite_stat3 table

略。

3. The Rollback Journal

略。

2.6.6. The sqlite_stat4 table

略。

4. The Write-Ahead Log

略。

其他

.headers on
.mode column
PRAGMA table_info(table_name)

或者

select * from pragma_table_info("table_name")

(草草收尾)