SQLite 嵌入式——零配置的极致轻量 / SQLite as a Lightweight Embedded Database
📅 创建时间:2026-05-08 🏷️ 标签:#SQLite #嵌入式 #WAL #PGlite #Electron #移动端 📚 前置知识:[[00-db-overview]] [[01-mysql]](了解数据库基本概念) 📚 相关知识:[[17-lobechat-design-analysis]](PGlite 实践)
SQLite 定位速览
┌─────────────────────────────────────────────────────────────┐
│ SQLite 在数据库版图中的位置 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 部署量:地球上安装最多的数据库(手机、电视、汽车、飞机...) │
│ 定位: 嵌入式零配置数据库,"数据库即文件" │
│ 本质区别: 单进程数据库,不是客户端/服务器架构 │
│ 适用场景: 移动端、浏览器、桌面应用、单用户场景、测试 │
│ 不适合: 多用户并发写入、需要水平扩展 │
│ │
│ 一句话总结: │
│ SQLite 是唯一一个用户数比 MySQL 更多的数据库。 │
│ 它就在你手机里的每一个 App 里。 │
│ │
└─────────────────────────────────────────────────────────────┘第1部分:架构哲学——为什么 SQLite 如此特殊
1.1 客户端/服务器 vs 嵌入式
┌─────────────────────────────────────────────────────────────┐
│ 传统 RDBMS 架构 vs SQLite 架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 传统 RDBMS(MySQL / PostgreSQL): │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 客户端 │ ───▶ │ 服务器 │ ───▶ │ 数据库 │ │
│ │ (Driver) │ │ (Server) │ │ 进程/服务 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ • 需要安装数据库服务(mysqld / postgres) │
│ • 网络协议通信(TCP/IP) │
│ • 多连接并发 │
│ │
│ SQLite: │
│ ┌──────────┐ ┌──────────┐ │
│ │ 应用程序 │ ───▶ │ 数据库 │ │
│ │ │ │ (文件) │ │
│ │ libsqlite│ │ app.db │ │
│ └──────────┘ └──────────┘ │
│ • 库直接链接到应用(无独立进程) │
│ • 直接读写文件(无网络层) │
│ • 单写多读(默认) │
│ │
└─────────────────────────────────────────────────────────────┘1.2 SQLite 文件结构
┌─────────────────────────────────────────────────────────────┐
│ SQLite 数据库文件结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ app.sqlite(数据库文件) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Header (100 bytes) │ │
│ │ ├─ magic: "SQLite format 3" │ │
│ │ ├─ page_size: 4096 │ │
│ │ ├─ version: 4 (WAL mode) │ │
│ │ └─ ... │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Page 1: sqlite_master (表结构) │ │
│ │ Page 2: users 表数据 │ │
│ │ Page 3: orders 表数据 │ │
│ │ Page N: ... │ │
│ │ ├─ B-tree 叶子节点(行数据) │ │
│ │ └─ B-tree 内部节点(索引) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ app.sqlite-wal(WAL 日志,可选) │
│ app.sqlite-shm(共享内存,可选) │
│ │
│ 特点: │
│ • 数据库就是一个文件,可以直接复制、移动、备份 │
│ • 支持跨平台(Windows / Linux / macOS / Android / iOS) │
│ • 一个文件可以包含多个表和索引 │
│ │
└─────────────────────────────────────────────────────────────┘第2部分:WAL 模式——解决并发写入的关键
2.1 三种事务模式
┌─────────────────────────────────────────────────────────────┐
│ SQLite 事务模式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ DELETE 模式(默认,最老): │
│ • 写入时加排他锁,其他连接全部阻塞 │
│ • 写操作期间读也不允许 │
│ • 高并发写入性能极差 │
│ │
│ TRUNCATE 模式: │
│ • 写操作不加排他锁 │
│ • 但同一时刻只能一个写入 │
│ • 速度较快,但不常用 │
│ │
│ WAL 模式(推荐): │
│ • 写入和读取可以完全并发 │
│ • 写入记录到 WAL 日志,读取访问主数据库 │
│ • WAL 日志定期与主数据库合并(checkpoint) │
│ • 支持多个读连接 + 一个写连接 │
│ • 几乎所有高并发场景都必须用 WAL │
│ │
└─────────────────────────────────────────────────────────────┘2.2 WAL 实战配置
sql
-- 开启 WAL 模式(性能提升显著)
PRAGMA journal_mode = WAL;
-- 设置 WAL 自动 checkpoint 大小(默认 1000 页)
PRAGMA wal_autocheckpoint = 1000;
-- 设置 busy_timeout(写锁等待时间)
PRAGMA busy_timeout = 5000;
-- 查看当前模式
PRAGMA journal_mode;
-- 查看 WAL 状态
PRAGMA wal_checkpoint;
-- 手动 checkpoint(WAL → 主数据库合并)
PRAGMA wal_checkpoint(TRUNCATE);第3部分:SQLite 进阶特性
3.1 JSON 支持
sql
-- SQLite JSON 函数
SELECT json_extract('{"name":"张三","age":30}', '$.name'); -- 张三
-- JSON 数组
SELECT json_array_length('[1,2,3]'); -- 3
-- JSON 树形查询
SELECT value FROM json_each('{"a":1,"b":2}');
-- value: 1, 2
-- 存储和查询 JSON
CREATE TABLE config (
id INTEGER PRIMARY KEY,
data TEXT -- 存 JSON 字符串
);
INSERT INTO config VALUES (1, '{"theme":"dark","lang":"zh"}');
-- 查询 JSON 字段
SELECT * FROM config
WHERE json_extract(data, '$.theme') = 'dark';3.2 全文搜索(FTS5)
sql
-- 创建全文搜索虚拟表
CREATE VIRTUAL TABLE articles_fts USING fts5(
title,
content,
content='articles', -- 关联到原始表
content_rowid='id'
);
-- 插入数据(触发器自动同步)
INSERT INTO articles_fts(rowid, title, content)
SELECT id, title, content FROM articles;
-- 全文搜索
SELECT * FROM articles
WHERE rowid IN (
SELECT rowid FROM articles_fts
WHERE articles_fts MATCH 'PostgreSQL 数据库'
)
ORDER BY rank;
-- 高级搜索
SELECT * FROM articles_fts
WHERE articles_fts MATCH '"SQL 数据库" NOT MySQL';
-- 搜索建议(自动补全)
SELECT * FROM articles_fts
WHERE articles_fts MATCH 'Post*';3.3 窗口函数(SQLite 3.25+)
sql
-- SQLite 支持完整的窗口函数
SELECT
name,
department,
salary,
AVG(salary) OVER (PARTITION BY department) as dept_avg,
salary - AVG(salary) OVER (PARTITION BY department) as vs_avg
FROM employees;第4部分:SQLite 的限制与误区
┌─────────────────────────────────────────────────────────────┐
│ SQLite 限制清单 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ❌ 不适合高并发写入: │
│ 默认单写进程,WAL 模式下一个时刻只有一个写入者 │
│ 百万级并发写入 → 应该用 PostgreSQL / TiDB │
│ │
│ ❌ 不适合复杂分析: │
│ 缺乏并行查询引擎,分析型场景 → DuckDB / ClickHouse │
│ │
│ ❌ 无网络协议: │
│ 无法远程访问,只能本地文件 → 需要多用户 → PostgreSQL │
│ │
│ ❌ 写入放大: │
│ WAL + checkpoint 机制,频繁写入会产生碎片 │
│ 需要定期 VACUUM │
│ │
│ ✅ 适合的场景: │
│ • 移动 App 本地存储(iOS/Android) │
│ • 桌面应用(Electron、Qt、Electron 的 PGlite) │
│ • 浏览器扩展 / 小型网站(WordPress 用 SQLite) │
│ • 测试数据库(无需安装任何东西) │
│ • 数据分析预处理(CSV → SQLite → DuckDB) │
│ • 嵌入式 Linux 设备 │
│ │
└─────────────────────────────────────────────────────────────┘sql
-- 性能优化:定期维护
PRAGMA auto_vacuum = INCREMENTAL; -- 增量 vacuum
VACUUM; -- 手动整理碎片(生产数据下很慢,建议定期执行)
-- 批量写入优化
PRAGMA synchronous = NORMAL; -- 安全性和性能的折中
PRAGMA cache_size = -64000; -- 64MB 缓存(负数 = KB)
PRAGMA temp_store = MEMORY; -- 临时表放内存
-- BEGIN TRANSACTION 批量插入(必须!)
BEGIN;
INSERT INTO logs VALUES (1, 'msg1');
INSERT INTO logs VALUES (2, 'msg2');
INSERT INTO logs VALUES (3, 'msg3');
... -- 1000 条
COMMIT;
-- 无事务:每条都是独立事务(极慢)
-- 有事务:1 个事务(快 10-100x)第5部分:PGlite——SQLite 走进现代应用
5.1 PGlite 是什么
PGlite = PostgreSQL 编译为 WebAssembly,运行在浏览器/Electron 中:
┌─────────────────────────────────────────────────────────────┐
│ PGlite 技术架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 传统方案: │
│ Web App → 网络 → PostgreSQL 服务器 → 返回结果 │
│ │
│ PGlite 方案: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 浏览器 / Electron │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ PGlite (WASM) │ │ │
│ │ │ ┌─────────────────────────────────────────┐ │ │ │
│ │ │ │ PostgreSQL 17(完整) │ │ │ │
│ │ │ │ • 事务(ACID) │ │ │ │
│ │ │ │ • pgvector(向量检索) │ │ │ │
│ │ │ │ • JSONB │ │ │ │
│ │ │ │ • 全文本搜索 FTS5 │ │ │ │
│ │ │ └─────────────────────────────────────────┘ │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ LobeChat 桌面版使用 PGlite,替代传统的 SQLite 或 IndexedDB │
│ 一套代码同时支持:PostgreSQL(服务器) + PGlite(桌面) │
│ │
└─────────────────────────────────────────────────────────────┘5.2 PGlite 使用示例
typescript
// Node.js / Electron 中使用 PGlite
import { PGlite } from '@electric-sql/pglite';
const db = new PGlite(); // 内存数据库
// const db = new PGlite('/path/to/data'); // 文件持久化
// 等待初始化
await db.waitReady;
// 执行 SQL
await db.exec(`
CREATE TABLE IF NOT EXISTS messages (
id SERIAL PRIMARY KEY,
content TEXT,
created_at TIMESTAMP DEFAULT NOW()
)
`);
// 插入数据
await db.query(
'INSERT INTO messages (content) VALUES ($1)',
['Hello PGlite!']
);
// 查询
const result = await db.query('SELECT * FROM messages');
console.log(result.rows); // [{id:1, content:'Hello PGlite!', ...}]
// 流式查询(大数据集)
for await (const row of db.query('SELECT * FROM messages')) {
console.log(row);
}
// 关闭连接
await db.close();第6部分:Python / Node.js 实战
python
# Python: 使用 sqlite3 内置模块(无需安装)
import sqlite3
conn = sqlite3.connect('app.db')
cursor = conn.cursor()
# 创建表
cursor.execute('''
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
status TEXT DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# 批量插入(带事务)
tasks = [
('完成报告', 'completed'),
('回复邮件', 'pending'),
('代码审查', 'in_progress'),
]
cursor.executemany(
'INSERT INTO tasks (title, status) VALUES (?, ?)',
tasks
)
conn.commit()
# 查询
cursor.execute('''
SELECT * FROM tasks
WHERE status = ?
ORDER BY created_at DESC
''', ('pending',))
for row in cursor.fetchall():
print(f'{row[0]}: {row[1]} [{row[2]}]')
conn.close()javascript
// Node.js: 使用 better-sqlite3(同步,性能最佳)
import Database from 'better-sqlite3';
const db = new Database('app.db', { verbose: console.log });
// 启用 WAL
db.pragma('journal_mode = WAL');
db.pragma('synchronous = NORMAL');
// 预处理语句(防注入)
const insert = db.prepare(
'INSERT INTO messages (content, role) VALUES (?, ?)'
);
const insertMany = db.transaction((rows) => {
for (const row of rows) insert.run(row.content, row.role);
});
insertMany([
{ content: 'Hello', role: 'user' },
{ content: 'Hi!', role: 'assistant' },
]);
// 查询
const rows = db.prepare(
'SELECT * FROM messages ORDER BY created_at'
).all();
console.log(rows);学习状态:🟡 开始学习