SQLite 全面解析

世界上最广泛部署的数据库引擎 — 深入理解其架构与原理

零配置单文件数据库ACID 事务嵌入式部署<500KB

什么是 SQLite?

SQLite进程内的关系型数据库(RDBMS),将整个数据库存储在单个磁盘文件中,不需要独立的数据库服务器进程。

🚀 嵌入式设计

直接编译到应用程序中,库只有 <500KB。

📄 单文件存储

整个数据库就是一个 .db 文件,复制即可备份。

🔒 ACID 合规

通过 journal 或 WAL 完整支持事务。

⏰ 零配置

无需安装、无需配置、开箱即用。

部署规模

3 万亿+

全球活跃部署设备

1 万亿+

每日查询量

0

配置项(零配置)

<500KB

完整库文件大小

发展时间线

2000 年 — D. Richard Hipp 创建,为美国海军导弹驱逐舰导航系统开发

2005 年 — 进入 Firefox、Android、iOS,成为移动端标准

2010 年 — WAL 模式引入,提升并发性能

至今 — 全世界最广泛部署的数据库引擎

SQLite 的核心优势

1. 零网络延迟

传统数据库(MySQL/PG)应用程序数据库服务器TCP/IP 网络延迟:1~50 msSQLite(嵌入式)应用程序 + SQLite 引擎(同一进程内)app.db(磁盘文件)延迟:<0.1 ms(本地 I/O)

2. 适用场景

📱 移动应用

iOS(Core Data 底层)、Android、Flutter、React Native 的默认本地数据库

🖥️ 桌面应用

VS Code、Chrome、Skype 等均使用 SQLite 存储本地数据

🌐 小型 Web

个人博客、低流量网站、Django/Flask 默认 SQLite

📊 数据分析

CSV 太大无法 Excel 处理时,导入 SQLite 用 SQL 分析

3. ACID 事务保证

A — 原子性

事务要么全部成功,要么全部回滚。

C — 一致性

事务前后数据库始终处于合法状态。

I — 隔离性

并发事务互不干扰,通过文件锁实现。

D — 持久性

COMMIT 后数据永久保存,崩溃也不丢失。

SQLite vs 其他数据库

SQLite

嵌入式数据库

VS

MySQL / PostgreSQL

客户端-服务器数据库

特性SQLiteMySQLPostgreSQL
部署模式嵌入式(进程内)客户端-服务器客户端-服务器
安装体积<500 KB~100 MB~50 MB
并发写入单写多读高并发高并发
网络访问不支持原生支持原生支持
数据规模~140 TB(理论)TB~PBTB~PB
用户权限文件系统级细粒度 SQL 权限细粒度 SQL 权限
崩溃恢复自动(journal/WAL)依赖配置依赖配置

选型建议:

✅ 单用户、移动端、桌面应用 → SQLite

✅ 多用户 Web 服务、高并发写入 → MySQL / PostgreSQL

✅ 需要 JSON、GIS、复杂分析 → PostgreSQL

架构原理

整体架构分层

应用程序层SQLite C APISQL 编译层(Tokenizer → Parser → CodeGen → VM)核心引擎B-Tree 模块Pager 模块WAL 管理虚拟文件系统(VFS)Unix VFS(read/write/fsync/mmap)Windows VFS(Win32 API)内存 VFS(:memory:)磁盘文件(app.db / app.db-wal)

各层核心职责

1. SQL 编译层

将 SQL 文本编译为 SQLite 虚拟机字节码(~180 种 opcode)。

-- 查看编译后的字节码
EXPLAIN SELECT name FROM users WHERE id = 1;
-- 输出:Init / OpenRead / Rewind / Column / ResultRow / Halt ...

2. B-Tree 模块

每个表和索引对应一棵独立的 B-Tree。默认页大小 4096 字节,可通过 PRAGMA page_size 调整(512~65536)。

3. Pager 模块

页面缓存管理器:负责将磁盘页读入内存缓存、脏页写回、journal/WAL 管理、崩溃恢复。

4. VFS 层

抽象操作系统文件接口,支持 Unix / Windows / 内存 / WebAssembly 等平台。

B-Tree 索引结构

核心概念:SQLite 使用 B*-Tree 组织数据。每个节点 = 一个 4KB 页。表数据用「表 B-Tree」(叶子存完整行),索引用「索引 B-Tree」(叶子存键 + rowid 指针)。

页结构可视化

Page Header(8~20 字节)页类型 | 首空闲块 | 细胞数 | 细胞起始 | 碎片字节数Cell Pointer ArrayPointer 1 → 0x0080Pointer 2 → 0x0150Pointer 3 → 0x0210...(每指针2字节,按key升序)Cell Payload(实际数据)Cell 1: [payload_len] [key] [data bytes...]Cell 2: [payload_len] [key] [data bytes...]...Unallocated Free Space(VACUUM 可回收碎片空间)

内部分叉页 vs 叶子页

🔹 内部分叉页(Interior)

// 格式:left_child | key | right_child
// 作用:导航定位,不存实际数据

🔹 叶子页(Leaf)

// 格式:[payload_len] [key] [payload_data]
// 表叶子页:payload = 完整行记录
// 索引叶子页:payload = 索引键 + rowid

交互式 B-Tree 查询演示 — 查找 key=35

[ 25 | 50 ][ 10 | 20 ][ 30 | 40 ][ 60 | 80 ]5 10 15 2025 28 3035 38 4050 55 6070 80 90

事务机制与 ACID 实现

原子性 — Rollback Journal vs WAL

原始数据库数据页 A数据页 B复制原页回滚日志(journal)原始页A副本原始页B副本写入新页修改后数据库新数据页A新数据页BCOMMIT✓ 删除日志↩ 恢复

Journal 模式:先将原始页写入日志,再修改主库。崩溃时用日志恢复。

主数据库(app.db)已提交数据页WAL 文件(app.db-wal)Frame 1: 新页 AFrame 2: 新页 BWAL Indexpage1 → Frame1page3 → Frame2读取 = 主库 + WAL 合并视图(读写并行)CHECKPOINT:定期将 WAL 帧写回主库

WAL 模式:写入追加到 WAL 文件,读操作合并主库和 WAL,读写可并行。

隔离性 — 文件锁状态机

SQLite 通过文件锁实现事务隔离:

// SQLite 文件锁状态机(简化)
UNLOCKED → SHARED → RESERVED → PENDING → EXCLUSIVE
  (无锁)      (可读)     (计划写)    (等待)    (排他写)
锁状态可进行操作说明
UNLOCKED初始状态
SHARED并发读取多连接可同时持有,阻塞 EXCLUSIVE
RESERVED读取+本地写入一个连接持有,不阻塞其他 SHARED
PENDING等待升级阻止新 SHARED,等现有 SHARED 释放
EXCLUSIVE写入排他锁,阻塞所有其他连接

WAL 模式详解

WAL(Write-Ahead Logging) 是 SQLite 3.7.0 引入的默认日志模式,核心思想:先写日志,再写主库,读操作合并主库和 WAL,实现读写并行。

✅ 读不阻塞写

读访问主库+WAL,写只追加 WAL,两者并行。

✅ 写入吞吐更高

顺序追加写入 WAL,避免随机写,批量 checkpoint。

⚠️ 注意事项

WAL 文件可能增长;需定期 CHECKPOINT;:memory: 不支持。

WAL 交互演示

主数据库
页1, 页2, 页3
WAL 文件
0 帧

启用 WAL

-- 开启 WAL 模式(每个连接都需要执行或设为默认)
PRAGMA journal_mode = WAL;

-- 设置 WAL 自动 checkpoint 阈值(默认 1000 页)
PRAGMA wal_autocheckpoint = 1000;

-- 手动 FULL checkpoint
PRAGMA wal_checkpoint(FULL);

-- 查看当前 journal 模式
PRAGMA journal_mode;

SQLite 文件格式深入

数据库文件整体布局

Page 1: sqlite_master 表type | name | tbl_name | rootpage | sql记录所有表、索引、视图、触发器定义空闲页管理(Freelist)删除数据后形成的空闲页链表VACUUM 可回收这些空间用户表 B-Tree(页 2~N)叶子页存储完整行数据INTEGER PRIMARY KEY → rowid 隐式聚簇索引 B-Tree(页 N+1~)叶子页存储:索引键 + rowidWHERE 条件通过索引避免全表扫描每个页 = page_size(默认 4096 字节),page 1 是 sqlite_master

记录格式(Record Format)

SQLite 使用可变长度记录格式(基于 varint 编码),高效存储 NULL、INTEGER、REAL、TEXT、BLOB:

// 一条记录的实际磁盘格式
[header_size_varint] [type_sequence_varints...] [data_bytes...]
// type varint: 0=NULL, 1=int8, 2=int16, 3=int24, 4=int32, 5=int48,
//              6=int64, 7=double, 8=0, 9=1, >=12=text/blob
// 示例:INSERT INTO t VALUES(1, 'hello');
// 记录头:varint(头部大小) + varint(1) + varint(13)
// 数据区:0x01 0x05 'h' 'e' 'l' 'l' 'o'

VACUUM — 文件整理

-- VACUUM 重建整个数据库文件,回收碎片和空闲页
-- 注意:需要约 2 倍数据库大小的临时磁盘空间
VACUUM;

-- 只整理特定表(SQLite 3.36+)
VACUUM main.TABLE users;

交互式 SQL 演示

SQL 语句
-- 点击上方按钮查看 SQL 演示
执行结果

核心要点总结

🚀 架构简洁

进程内运行,单文件存储,零配置部署,<500KB 体积。

🔐 ACID 可靠

Journal/WAL 双模式保障原子性和持久性,崩溃自动恢复。

📊 B-Tree 高效

4KB 页组织,O(log n) 查询,支持索引加速。

⚡ WAL 并发

写前日志模式,读写并行,写入吞吐大幅提升。

概念速查表

概念作用类比
B-Tree索引与数据组织结构字典的拼音目录
Page (4KB)磁盘读写的基本单位书本的每一页
Pager页面缓存与 I/O 管理图书管理员
WAL写操作日志,支持并发读写草稿本 + 定稿流程
Journal回滚日志,保障原子性游戏存档点
VACUUM整理磁盘空间,回收碎片磁盘清理工具
rowidINTEGER 主键的隐式聚簇索引书本的行号

延伸阅读推荐:

📖 SQLite 官方文档:sqlite.org/arch.html

📖 SQLite 文件格式:sqlite.org/fileformat2.html

📖 WAL 模式详解:sqlite.org/wal.html