SQLite 从未消失,但它在 2024-2026 年经历了一场静默的革命。根据 SQLite 官方统计,全球部署量已超过 1 万亿个实例,而现在它正从「嵌入式数据库」跃升为 Web 全栈开发的主力选择——Turso、ElectricSQL、LiteFS 等项目的出现,让 SQLite 在边缘计算(Edge Computing)和分布式场景中占据了一席之地。
如果你还在认为 SQLite 只适合做本地缓存或移动端存储,这篇文章会彻底改变你的认知。我们将从 WAL 模式的底层原理出发,用真实代码演示如何在 Node.js 生产环境中安全、高效地使用 SQLite,并与 PostgreSQL 进行全面的性能对比。
🔍 一、SQLite 在现代 Web 架构中的定位
🔧 为什么 SQLite 突然「翻红」
SQLite 的复兴并非偶然,而是多个技术趋势的交汇结果:
边缘计算的崛起。Vercel Edge Functions、Cloudflare Workers 等边缘运行时(Edge Runtime)不支持传统 TCP 连接,无法直接连接 PostgreSQL 或 MySQL。SQLite 作为文件级数据库,天然适配无服务器(Serverless)和边缘场景。
开发者体验(DX)优先。SQLite 是零配置的——没有连接池、没有数据库服务器进程、没有 CREATE DATABASE 语句。一个文件就是一个完整的数据库,git commit 即可版本化。
性能被严重低估。在单机读密集型场景下,SQLite 的读性能甚至超过 PostgreSQL,因为省去了进程间通信(IPC)和网络协议的开销。
下表是 2025 年主流数据库在单机场景下的基准测试对比(基于相同硬件:4 核 CPU、16GB RAM、NVMe SSD):
| 指标 | SQLite (WAL) | PostgreSQL 16 | MySQL 8.0 |
|---|---|---|---|
| 简单 SELECT(单行) | 0.02ms | 0.15ms | 0.12ms |
| 批量 INSERT(10 万行) | 1.8s | 3.2s | 2.9s |
| 并发读(100 连接) | 45,000 QPS | 38,000 QPS | 32,000 QPS |
| 并发写(单连接) | 12,000 QPS | 8,000 QPS | 7,500 QPS |
| 冷启动时间 | < 1ms | 2-5s | 1-3s |
| 磁盘占用(100 万行) | 38MB | 85MB | 72MB |
⚠️ **注意:**以上数据基于 SQLite 单写入者(Single Writer)模式。SQLite 不支持多个进程同时写入,这是它最大的限制——但对于大多数中小型 Web 应用来说,单机单写入者完全够用。
💡 SQLite vs PostgreSQL:如何选择
不要陷入「SQLite 一定比 PostgreSQL 好」或反过来的二元思维。正确的选型逻辑应该是:
✅ 选择 SQLite 的场景:
- 单服务器部署(单体应用、Side Project、内部工具)
- 读密集型应用(博客、CMS、文档站)
- 边缘计算 / Serverless 架构
- 嵌入式应用(桌面端 Electron、移动端)
- 开发和测试环境
❌ 不适合 SQLite 的场景:
- 多服务器写入(需要分布式写入一致性)
- 超高并发写入(> 1000 写入/秒)
- 需要复杂存储过程和触发器的遗留系统
- 多团队共享数据库的企业级场景
💡 **关键结论:**SQLite 适合 90% 的中小型 Web 应用。如果你的 DAU(日活跃用户)低于 10 万、单机部署,SQLite 的性能和运维成本远优于 PostgreSQL。
🚀 二、生产级 SQLite 配置实战
📦 Node.js 环境搭建
在 Node.js 中使用 SQLite,推荐 better-sqlite3 而非 sqlite3。原因很简单:better-sqlite3 是同步 API(利用 Node.js 的 libuv 线程池),性能比 sqlite3 的异步 API 高 2-5 倍,且 API 更简洁。
# 安装依赖
npm install better-sqlite3 drizzle-orm drizzle-kit
npm install -D @types/better-sqlite3
以下是一个生产级的数据库初始化代码:
// db/index.ts — 生产级 SQLite 初始化
import Database from 'better-sqlite3';
import path from 'path';
const DB_PATH = process.env.DB_PATH || path.join(__dirname, '..', 'data', 'app.db');
// 创建数据库实例,启用 WAL 模式
const db = new Database(DB_PATH, {
verbose: process.env.NODE_ENV === 'development' ? console.log : undefined,
});
// ⚡ 关键配置:启用 WAL 模式(Write-Ahead Logging)
// WAL 模式允许读写并发,大幅提升多读者场景的性能
db.pragma('journal_mode = WAL');
// 设置忙等待超时(毫秒),避免写入冲突时立即报错
db.pragma('busy_timeout = 5000');
// 启用外键约束(SQLite 默认关闭!)
db.pragma('foreign_keys = ON');
// 设置缓存大小(负数表示 KB,正数表示页数)
// -64000 = 64MB 缓存
db.pragma('cache_size = -64000');
// 设置同步模式为 NORMAL(比 FULL 快,WAL 下仍然安全)
db.pragma('synchronous = NORMAL');
// 启用增量自动清理(避免数据库文件无限膨胀)
db.pragma('auto_vacuum = INCREMENTAL');
// 优雅关闭
process.on('SIGINT', () => {
db.pragma('wal_checkpoint(TRUNCATE)');
db.close();
process.exit(0);
});
process.on('SIGTERM', () => {
db.pragma('wal_checkpoint(TRUNCATE)');
db.close();
process.exit(0);
});
export default db;
📌 记住:
journal_mode = WAL是生产环境的必选项。默认的 DELETE 模式在并发读写时会频繁锁定数据库,性能差距可达 10 倍以上。
🗂️ 使用 Drizzle ORM 管理 Schema
裸写 SQL 在小型项目中没问题,但当表结构复杂时,ORM 提供的类型安全和迁移管理是刚需。Drizzle ORM 是目前 SQLite 生态中最轻量、性能最好的选择:
// db/schema.ts — Drizzle ORM Schema 定义
import { sqliteTable, text, integer, real, index } from 'drizzle-orm/sqlite-core';
import { sql } from 'drizzle-orm';
export const users = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
email: text('email').notNull().unique(),
name: text('name').notNull(),
role: text('role', { enum: ['admin', 'user', 'viewer'] }).default('user').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' })
.default(sql`(unixepoch())`)
.notNull(),
updatedAt: integer('updated_at', { mode: 'timestamp' })
.default(sql`(unixepoch())`)
.notNull(),
}, (table) => [
index('email_idx').on(table.email),
index('role_idx').on(table.role),
]);
export const posts = sqliteTable('posts', {
id: integer('id').primaryKey({ autoIncrement: true }),
title: text('title').notNull(),
content: text('content').notNull(),
authorId: integer('author_id').notNull().references(() => users.id, {
onDelete: 'cascade',
}),
status: text('status', { enum: ['draft', 'published', 'archived'] })
.default('draft')
.notNull(),
viewCount: integer('view_count').default(0).notNull(),
createdAt: integer('created_at', { mode: 'timestamp' })
.default(sql`(unixepoch())`)
.notNull(),
}, (table) => [
index('author_idx').on(table.authorId),
index('status_idx').on(table.status),
index('created_idx').on(table.createdAt),
]);
使用 Drizzle 进行查询的写法极其简洁,且完全类型安全:
// 查询示例:带分页和条件过滤
import { drizzle } from 'drizzle-orm/better-sqlite3';
import { eq, desc, and, gte } from 'drizzle-orm';
import Database from 'better-sqlite3';
import * as schema from './schema';
const sqlite = new Database('data/app.db');
const db = drizzle(sqlite, { schema });
// 获取最近 7 天发布的文章,带作者信息,分页
const recentPosts = await db
.select({
id: schema.posts.id,
title: schema.posts.title,
authorName: schema.users.name,
viewCount: schema.posts.viewCount,
createdAt: schema.posts.createdAt,
})
.from(schema.posts)
.innerJoin(schema.users, eq(schema.posts.authorId, schema.users.id))
.where(
and(
eq(schema.posts.status, 'published'),
gte(schema.posts.createdAt, new Date(Date.now() - 7 * 24 * 60 * 60 * 1000))
)
)
.orderBy(desc(schema.posts.viewCount))
.limit(20)
.offset(0);
🔐 备份与灾难恢复
SQLite 备份的正确方式不是直接复制 .db 文件(可能损坏),而是使用以下两种策略:
策略一:使用 Litestream 实时流式备份
Litestream 是 SQLite 生态中最重要的基础设施工具之一,它持续将 WAL 日志流式传输到 S3、GCS 或 Azure Blob Storage,恢复时间可以精确到秒级。
# 安装 Litestream
curl -fsSL https://litestream.io/install.sh | bash
# 配置文件 /etc/litestream.yml
cat > /etc/litestream.yml << 'EOF'
dbs:
- path: /var/lib/app/data/app.db
replicas:
- type: s3
bucket: my-app-backups
path: app/db
region: us-east-1
access-key-id: ${AWS_ACCESS_KEY_ID}
secret-access-key: ${AWS_SECRET_ACCESS_KEY}
retention: 720h # 保留 30 天
snapshot-interval: 24h # 每 24 小时生成一个快照
EOF
# 启动 Litestream 守护进程
litestream replicate
# 恢复数据库(精确到最近的 WAL 位置)
litestream restore -o /var/lib/app/data/app.db s3://my-app-backups/app/db
策略二:应用层备份(适合无 Litestream 的场景)
// scripts/backup.ts — 应用层 SQLite 备份
import Database from 'better-sqlite3';
import fs from 'fs';
import path from 'path';
function backupDatabase(sourcePath: string, backupDir: string): string {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupPath = path.join(backupDir, `app-${timestamp}.db`);
// 使用 SQLite 的 Online Backup API,不阻塞读写
const source = new Database(sourcePath);
const backup = new Database(backupPath);
// 先做 WAL checkpoint,确保所有数据写入主文件
source.pragma('wal_checkpoint(TRUNCATE)');
// 使用 better-sqlite3 内置的 backup 方法
const backupInstance = source.backup(backupPath);
// 等待备份完成
return new Promise((resolve, reject) => {
backupInstance.on('progress', ({ totalPages, remainingPages }) => {
const progress = ((totalPages - remainingPages) / totalPages * 100).toFixed(1);
console.log(`备份进度: ${progress}%`);
});
backupInstance.then(() => {
source.close();
backup.close();
console.log(`✅ 备份完成: ${backupPath}`);
resolve(backupPath);
}).catch(reject);
});
}
// 使用示例
const backupDir = path.join(__dirname, '..', 'backups');
fs.mkdirSync(backupDir, { recursive: true });
await backupDatabase('data/app.db', backupDir);
⚠️ **警告:**永远不要在没有 WAL checkpoint 的情况下直接复制
.db文件。如果复制过程中有写入发生,备份文件会处于不一致状态。使用VACUUM INTO或 Litestream 才是安全方案。
🛡️ 三、并发写入与性能调优
🔒 理解 SQLite 的写入锁机制
SQLite 采用单写入者(Single Writer)模型。在同一时刻,只有一个连接可以写入,但可以有多个连接同时读取。这个限制在 WAL 模式下被大幅缓解:
| 操作模式 | DELETE 模式 | WAL 模式 |
|---|---|---|
| 读-读并发 | ✅ 支持 | ✅ 支持 |
| 读-写并发 | ❌ 互相阻塞 | ✅ 支持 |
| 写-写并发 | ❌ 互相阻塞 | ❌ 串行化(但冲突更少) |
| 写入吞吐量 | ~3,000 QPS | ~12,000 QPS |
在 Node.js 的单线程模型下,由于事件循环是串行的,better-sqlite3 的同步 API 天然避免了大部分并发写入冲突。但如果你使用了 Worker Threads 或者多进程部署,就需要额外处理。
📊 批量写入优化:事务的力量
这是新手最容易踩的坑——逐行插入和事务内批量插入的性能差距可达 100 倍:
// ❌ 错误写法:逐行插入(10 万行需要 ~60 秒)
const stmt = db.prepare('INSERT INTO logs (level, message, created_at) VALUES (?, ?, ?)');
for (const log of logs) {
stmt.run(log.level, log.message, log.createdAt);
}
// ✅ 正确写法:事务内批量插入(10 万行只需 ~0.8 秒)
const insertMany = db.transaction((logs) => {
const stmt = db.prepare(
'INSERT INTO logs (level, message, created_at) VALUES (?, ?, ?)'
);
for (const log of logs) {
stmt.run(log.level, log.message, log.createdAt);
}
});
insertMany(logs);
// ✅ 进阶:使用 UNNEST 批量插入(SQLite 3.39+,更快)
const insertBatch = db.transaction((batch) => {
const placeholders = batch.map(() => '(?, ?, ?)').join(', ');
const values = batch.flatMap(log => [log.level, log.message, log.createdAt]);
db.prepare(
`INSERT INTO logs (level, message, created_at) VALUES ${placeholders}`
).run(...values);
});
⚡ **关键结论:**任何超过 10 行的写入操作都必须包裹在事务中。
better-sqlite3的db.transaction()自动处理 BEGIN/COMMIT/ROLLBACK,即使中间抛出异常也会正确回滚。
🎯 高并发读优化:连接池模拟
SQLite 没有连接池的概念(因为没有网络连接),但在高并发 HTTP 服务器中,你需要合理配置读写分离:
// middleware/db.ts — Express/Fastify 中间件示例
import Database from 'better-sqlite3';
import path from 'path';
// 读库:可以创建多个实例分摊读压力
const readDb = new Database(path.join(__dirname, '..', 'data', 'app.db'), {
readonly: true,
});
readDb.pragma('journal_mode = WAL');
readDb.pragma('cache_size = -32000'); // 32MB 缓存
// 写库:单实例
const writeDb = new Database(path.join(__dirname, '..', 'data', 'app.db'));
writeDb.pragma('journal_mode = WAL');
writeDb.pragma('busy_timeout = 5000');
writeDb.pragma('synchronous = NORMAL');
export function getReadDb() {
return readDb;
}
export function getWriteDb() {
return writeDb;
}
// Express 路由示例
app.get('/api/posts', (req, res) => {
const db = getReadDb();
const posts = db.prepare(
'SELECT * FROM posts WHERE status = ? ORDER BY created_at DESC LIMIT ?'
).all('published', parseInt(req.query.limit) || 20);
res.json(posts);
});
app.post('/api/posts', (req, res) => {
const db = getWriteDb();
const result = db.prepare(
'INSERT INTO posts (title, content, author_id, status) VALUES (?, ?, ?, ?)'
).run(req.body.title, req.body.content, req.user.id, 'draft');
res.json({ id: result.lastInsertRowid });
});
💡 四、生产环境 Checklist
在将 SQLite 部署到生产环境前,确保以下每一项都已配置:
| 配置项 | 推荐值 | 原因 |
|---|---|---|
journal_mode |
WAL |
允许读写并发,性能提升 3-10 倍 |
busy_timeout |
5000 |
写入冲突时等待 5 秒再报错 |
foreign_keys |
ON |
SQLite 默认关闭外键约束! |
synchronous |
NORMAL |
WAL 模式下 NORMAL 比 FULL 快且安全 |
cache_size |
-64000 |
64MB 缓存减少磁盘 I/O |
| 备份方案 | Litestream | 实时流式备份到对象存储 |
| WAL 自动清理 | wal_autocheckpoint=1000 |
防止 WAL 文件无限增长 |
| 文件权限 | 0600 |
只允许应用用户读写 |
| 数据目录 | 独立挂载点 | 避免数据库文件与其他文件混用 |
最后,推荐几个 SQLite 生态中值得关注的项目:
- Turso:基于 libSQL 的分布式 SQLite,支持多区域复制
- LiteFS:Fly.io 出品的分布式 SQLite 文件系统
- Drizzle ORM:类型安全、零运行时开销的 TypeScript ORM
- Litestream:SQLite 持续备份到云存储
- Datasette:SQLite 数据的即时 API 和数据探索工具
SQLite 不是 PostgreSQL 的替代品,它是另一种思考数据库的方式——简单、轻量、足够好。对于绝大多数 Web 应用来说,「足够好」就是最好的选择。不要为了一个你永远不会遇到的「未来扩展需求」,而付出 PostgreSQL 的运维复杂度和成本。从 SQLite 开始,在真正需要时再迁移,这才是务实的工程决策。