正在加载,请稍候…

SQLite 生产环境实战:WAL 模式、并发写入与扩展策略

在 Web 服务中使用 SQLite 生产部署,涵盖 WAL 模式实现并发读取、正确加锁、Litestream 复制、Turso 分布式 SQLite,以及何时

SQLite 生产环境实战:WAL 模式、并发写入与扩展策略

SQLite 在生产环境中被低估

SQLite 每天在所有设备上执行超过 1 万亿次查询。借助现代工具,它完全可以用于生产级 Web 服务。

SQLite 生产环境实战:WAL 模式、并发写入与扩展策略 插图

为什么选择 SQLite 用于生产?

  • 无需独立服务器进程 — 部署更简单
  • 读取可无限扩展 — 文件可复制
  • 写入吞吐量:使用 WAL 模式可达 10 万+ 写入/秒
  • 零配置 — 无需连接池
  • 比等效的 PostgreSQL 数据小 35%

WAL 模式(必备)

import sqlite3

conn = sqlite3.connect('app.db')
cursor = conn.cursor()

# 启用 WAL 模式(对并发访问至关重要)
cursor.execute('PRAGMA journal_mode=WAL')

# 性能调优
cursor.execute('PRAGMA synchronous=NORMAL')  # 更快,WAL 模式下仍然安全
cursor.execute('PRAGMA cache_size=10000')    # 10MB 页面缓存
cursor.execute('PRAGMA temp_store=MEMORY')
cursor.execute('PRAGMA mmap_size=268435456') # 256MB 内存映射 I/O

conn.commit()

WAL 允许:

  • 多个并发读取器在有写入器时仍可工作
  • 一次只有一个写入器(串行化)
  • 读取性能不受写入器影响

SQLite 生产环境实战:WAL 模式、并发写入与扩展策略 插图

Better SQLite(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')
db.pragma('foreign_keys = ON')

// 预编译语句只编译一次,可重复使用
const getUser = db.prepare('SELECT * FROM users WHERE id = ?')
const insertUser = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)')
const updateUser = db.prepare('UPDATE users SET name = ? WHERE id = ?')

// 同步 API — 无需 async/await!
const user = getUser.get(42)
const result = insertUser.run('Alice', 'alice@example.com')
console.log(result.lastInsertRowid)  // 自增 ID

// 事务(原子性,快速 — 包装多个语句)
const transferPoints = db.transaction((fromId: number, toId: number, points: number) => {
  db.prepare('UPDATE users SET points = points - ? WHERE id = ?').run(points, fromId)
  db.prepare('UPDATE users SET points = points + ? WHERE id = ?').run(points, toId)
})

transferPoints(1, 2, 100) // 原子操作!

Litestream:持续复制

# litestream.yml
dbs:
  - path: /app/data/app.db
    replicas:
      - url: s3://my-bucket/app.db
        sync-interval: 1s
      - url: gcs://my-bucket/app.db  # Google Cloud Storage
# 后台复制到 S3
litestream replicate -config litestream.yml

# 从 S3 恢复(灾难恢复)
litestream restore -config litestream.yml -replica s3 /app/data/app.db

# Docker:与应用程序一起运行
CMD ["litestream", "replicate", "-exec", "node server.js"]

SQLite 生产环境实战:WAL 模式、并发写入与扩展策略 插图

Turso:分布式 SQLite

import { createClient } from '@libsql/client'

const db = createClient({
  url: 'libsql://my-db-username.turso.io',
  authToken: process.env.TURSO_AUTH_TOKEN,
})

// 相同的 SQLite API,全球分布式
const result = await db.execute('SELECT * FROM users WHERE id = ?', [42])
const users = result.rows

// 批量操作
await db.batch([
  { sql: 'INSERT INTO users (name) VALUES (?)', args: ['Alice'] },
  { sql: 'INSERT INTO users (name) VALUES (?)', args: ['Bob'] },
])

SQLite 与 PostgreSQL 的选择时机

场景 SQLite PostgreSQL
单服务器应用
读密集型 API ✅ 副本容易
写密集型(>1000 写入/秒)
复杂查询/分析 ⚠️ 有限
多租户 SaaS ✅ 每租户一个数据库 ⚠️
Edge 函数 ✅ Turso ❌ 太重
嵌入式/移动端 ✅ 原生

性能基准测试

// 插入 10 万行
const stmt = db.prepare('INSERT INTO events (type, data) VALUES (?, ?)')
const insert100k = db.transaction(() => {
  for (let i = 0; i < 100_000; i++) {
    stmt.run('click', JSON.stringify({ x: i, y: i }))
  }
})

const start = performance.now()
insert100k()
console.log(`100k inserts: ${performance.now() - start}ms`)
// ~200ms — 约 50 万插入/秒!