世界上最广泛部署的数据库引擎 — 深入理解其架构与原理
SQLite 是进程内的关系型数据库(RDBMS),将整个数据库存储在单个磁盘文件中,不需要独立的数据库服务器进程。
直接编译到应用程序中,库只有 <500KB。
整个数据库就是一个 .db 文件,复制即可备份。
通过 journal 或 WAL 完整支持事务。
无需安装、无需配置、开箱即用。
全球活跃部署设备
每日查询量
配置项(零配置)
完整库文件大小
2000 年 — D. Richard Hipp 创建,为美国海军导弹驱逐舰导航系统开发
2005 年 — 进入 Firefox、Android、iOS,成为移动端标准
2010 年 — WAL 模式引入,提升并发性能
至今 — 全世界最广泛部署的数据库引擎
iOS(Core Data 底层)、Android、Flutter、React Native 的默认本地数据库
VS Code、Chrome、Skype 等均使用 SQLite 存储本地数据
个人博客、低流量网站、Django/Flask 默认 SQLite
CSV 太大无法 Excel 处理时,导入 SQLite 用 SQL 分析
事务要么全部成功,要么全部回滚。
事务前后数据库始终处于合法状态。
并发事务互不干扰,通过文件锁实现。
COMMIT 后数据永久保存,崩溃也不丢失。
嵌入式数据库
客户端-服务器数据库
| 特性 | SQLite | MySQL | PostgreSQL |
|---|---|---|---|
| 部署模式 | 嵌入式(进程内) | 客户端-服务器 | 客户端-服务器 |
| 安装体积 | <500 KB | ~100 MB | ~50 MB |
| 并发写入 | 单写多读 | 高并发 | 高并发 |
| 网络访问 | 不支持 | 原生支持 | 原生支持 |
| 数据规模 | ~140 TB(理论) | TB~PB | TB~PB |
| 用户权限 | 文件系统级 | 细粒度 SQL 权限 | 细粒度 SQL 权限 |
| 崩溃恢复 | 自动(journal/WAL) | 依赖配置 | 依赖配置 |
选型建议:
✅ 单用户、移动端、桌面应用 → SQLite
✅ 多用户 Web 服务、高并发写入 → MySQL / PostgreSQL
✅ 需要 JSON、GIS、复杂分析 → PostgreSQL
将 SQL 文本编译为 SQLite 虚拟机字节码(~180 种 opcode)。
-- 查看编译后的字节码 EXPLAIN SELECT name FROM users WHERE id = 1; -- 输出:Init / OpenRead / Rewind / Column / ResultRow / Halt ...
每个表和索引对应一棵独立的 B-Tree。默认页大小 4096 字节,可通过 PRAGMA page_size 调整(512~65536)。
页面缓存管理器:负责将磁盘页读入内存缓存、脏页写回、journal/WAL 管理、崩溃恢复。
抽象操作系统文件接口,支持 Unix / Windows / 内存 / WebAssembly 等平台。
核心概念:SQLite 使用 B*-Tree 组织数据。每个节点 = 一个 4KB 页。表数据用「表 B-Tree」(叶子存完整行),索引用「索引 B-Tree」(叶子存键 + rowid 指针)。
// 格式:left_child | key | right_child // 作用:导航定位,不存实际数据
// 格式:[payload_len] [key] [payload_data] // 表叶子页:payload = 完整行记录 // 索引叶子页:payload = 索引键 + rowid
Journal 模式:先将原始页写入日志,再修改主库。崩溃时用日志恢复。
WAL 模式:写入追加到 WAL 文件,读操作合并主库和 WAL,读写可并行。
SQLite 通过文件锁实现事务隔离:
// SQLite 文件锁状态机(简化)
UNLOCKED → SHARED → RESERVED → PENDING → EXCLUSIVE
(无锁) (可读) (计划写) (等待) (排他写)
| 锁状态 | 可进行操作 | 说明 |
|---|---|---|
| UNLOCKED | 无 | 初始状态 |
| SHARED | 并发读取 | 多连接可同时持有,阻塞 EXCLUSIVE |
| RESERVED | 读取+本地写入 | 一个连接持有,不阻塞其他 SHARED |
| PENDING | 等待升级 | 阻止新 SHARED,等现有 SHARED 释放 |
| EXCLUSIVE | 写入 | 排他锁,阻塞所有其他连接 |
WAL(Write-Ahead Logging) 是 SQLite 3.7.0 引入的默认日志模式,核心思想:先写日志,再写主库,读操作合并主库和 WAL,实现读写并行。
读访问主库+WAL,写只追加 WAL,两者并行。
顺序追加写入 WAL,避免随机写,批量 checkpoint。
WAL 文件可能增长;需定期 CHECKPOINT;:memory: 不支持。
-- 开启 WAL 模式(每个连接都需要执行或设为默认) PRAGMA journal_mode = WAL; -- 设置 WAL 自动 checkpoint 阈值(默认 1000 页) PRAGMA wal_autocheckpoint = 1000; -- 手动 FULL checkpoint PRAGMA wal_checkpoint(FULL); -- 查看当前 journal 模式 PRAGMA journal_mode;
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 重建整个数据库文件,回收碎片和空闲页 -- 注意:需要约 2 倍数据库大小的临时磁盘空间 VACUUM; -- 只整理特定表(SQLite 3.36+) VACUUM main.TABLE users;
-- 点击上方按钮查看 SQL 演示进程内运行,单文件存储,零配置部署,<500KB 体积。
Journal/WAL 双模式保障原子性和持久性,崩溃自动恢复。
4KB 页组织,O(log n) 查询,支持索引加速。
写前日志模式,读写并行,写入吞吐大幅提升。
| 概念 | 作用 | 类比 |
|---|---|---|
| B-Tree | 索引与数据组织结构 | 字典的拼音目录 |
| Page (4KB) | 磁盘读写的基本单位 | 书本的每一页 |
| Pager | 页面缓存与 I/O 管理 | 图书管理员 |
| WAL | 写操作日志,支持并发读写 | 草稿本 + 定稿流程 |
| Journal | 回滚日志,保障原子性 | 游戏存档点 |
| VACUUM | 整理磁盘空间,回收碎片 | 磁盘清理工具 |
| rowid | INTEGER 主键的隐式聚簇索引 | 书本的行号 |
延伸阅读推荐:
📖 SQLite 官方文档:sqlite.org/arch.html
📖 SQLite 文件格式:sqlite.org/fileformat2.html
📖 WAL 模式详解:sqlite.org/wal.html