Skip to content

SQLite 详细介绍

SQLite 是零配置、文件型、嵌入式关系数据库,C语言开发,体积很小。 它不是客户端‑服务端数据库(MySQL/PostgreSQL是C/S架构),数据库就是一个普通磁盘文件,直接由程序读写这个文件,没有独立后台服务进程。

一句话:把数据库库链接进你的程序,程序直接读写 .db 文件。

核心特点

✅ 优点

  1. 无服务,零安装 没有守护进程,不需要启动服务,不需要账号密码。数据库就是单个文件,复制、备份、拷贝直接复制db文件即可;也支持内存数据库 :memory:,程序结束数据销毁。
  2. 嵌入式 库文件很小,静态编译后可以打包进应用。手机APP、浏览器、桌面软件、嵌入式设备大量使用。Rust、Python、Go都有成熟绑定。
  3. 完整SQL支持 支持标准SQL:表、索引、事务、JOIN、视图、触发器、外键、子查询。

默认外键约束默认关闭,需要手动开启。

  1. 单文件 整个数据库全部内容(表、索引、元数据)存放在一个磁盘文件。备份极其简单,复制文件。
  2. 事务ACID 完全支持ACID事务,崩溃安全。断电、程序异常不会损坏数据库文件。
  3. 跨平台 同一个 .db 文件,Windows / Linux / macOS / Android 直接通用。

⚠️ 短板(非常关键,选型重点)

  1. 全局写锁

SQLite 同一时刻只能有一个写事务;可以多个并发读。 多个线程/进程同时写,会排队;高并发写场景性能差,不适合高并发写业务。 读‑写会互斥,写的时候所有读会被阻塞。

  1. 不适合网络多客户端访问 不适合多台机器同时操作同一个db文件;文件锁在网络共享盘(NFS/SMB)不可靠。 适合:单服务进程访问;不适合:多实例微服务同时读写同一个db。
  2. 并发能力弱于 MySQL / PostgreSQL 读性能很好,写是瓶颈。大批量插入必须用事务,否则极慢。
  3. 数据库文件上限 理论最大文件 ~140TB,实际受操作系统限制。单表亿级数据可以跑,但索引设计很重要。
  4. 用户权限模型弱 没有MySQL那种用户、grant权限体系,权限完全靠操作系统文件权限控制db文件。

SQLite 存储模型

文件结构

  • 主数据库文件:xxx.db ,全部数据
  • xxx.db‑wal:WAL模式日志文件(Write‑Ahead‑Log)
  • xxx.db‑shm:WAL共享内存文件

WAL模式是现代SQLite默认模式,大幅提升并发读,写的时候读不受阻塞。

两种日志模式

  1. DELETE 旧模式 写操作直接覆盖原文件,写时阻塞读,现在很少用。
  2. WAL(预写日志)默认 修改先写入wal日志,不修改主db文件;读从主文件读取。
  • 写不会阻塞读,读不会阻塞写(但仍然只能一个写)
  • 定期 checkpoint 将wal合并入主db。>

开启WAL会多出两个附属文件,备份需要把三个文件一起复制。

数据类型(SQLite是动态类型,这点和别的数据库差异巨大)

SQLite 是弱类型数据库,列声明类型只是“建议”,可以存任意类型的值。 内部只有5种存储类:

  • NULL
  • INTEGER 有符号整数 1‑8字节
  • REAL 浮点数
  • TEXT UTF‑8字符串
  • BLOB 原始二进制字节
CREATE TABLE t(age INT);
INSERT INTO t(age) VALUES ("abc"); -- 可以插入字符串到INT列!不会报错

开发注意:业务层(Rust代码)做类型校验,不要完全依赖数据库约束。

常用类型映射 Rust <-> SQLite

SQLiteRust
INTEGERi64 / i32
TEXTString
REALf64
BLOBVec
NULLOption

重要开关 / PRAGMA 配置(Rust rusqlite中常用)

-- 开启外键约束,默认OFF!!
PRAGMA foreign_keys = ON;

-- 查看数据库版本
PRAGMA user_version;

-- WAL模式开启(现代默认)
PRAGMA journal_mode=WAL;

-- 同步模式 NORMAL / FULL / OFF
PRAGMA synchronous=NORMAL;

-- 设置缓存页大小
PRAGMA cache_size=-20000;

-- 手动执行checkpoint,把wal合并回主db
PRAGMA wal_checkpoint;

在 rusqlite 打开连接之后立刻执行 PRAGMA foreign_keys=ON;,否则外键完全不生效。

事务机制(性能重中之重)

SQLite 默认每条 INSERT/UPDATE/DELETE 自动开启提交事务。 循环插入1000条,不手动开事务,等于1000次事务提交,磁盘fsync,速度极其慢。

✅ 正确批量插入:显式开启事务

let tx = conn.transaction()?;
for item in items {
    tx.execute(..., params![...])?;
}
tx.commit()?; // 一次性提交

开启事务批量插入,速度可以提升上百倍

索引

和关系数据库一样:

CREATE INDEX idx_users_email ON users(email);

索引加速查询,但是降低写入速度。 UNIQUE约束会自动创建索引。

SQLite 适合什么场景

✅ 非常适合

  1. 桌面客户端APP、GUI程序
  2. 移动端APP
  3. 小型后端服务,单实例,读多写少
  4. 本地缓存、离线数据存储
  5. 单元测试内存数据库 :memory:
  6. 嵌入式设备
  7. 中小型数据量,单进程访问

❌ 不适合

  1. 多台服务器同时读写同一个db文件
  2. 极高并发写业务(每秒大量写入)
  3. 微服务多实例共享同一个数据库

Rust rusqlite 使用要点总结

  1. Connection 不是 Send,不能跨线程。多线程必须用连接池 r2d2‑rusqlite
  2. 打开连接第一件事执行 PRAGMA foreign_keys=ON;
  3. 大批量修改,务必包裹事务
  4. 禁止字符串拼接SQL,永远使用参数绑定 ?1 ?2,防止注入。
  5. SQLite写锁全局,连接池不要设置过大,一般4‑8足够。
  6. NULL字段 Rust 必须用 Option<T>
  7. WAL模式下备份要拷贝 .db .db‑wal .db‑shm 三个文件;或者执行 VACUUM INTO 'backup.db' 做在线备份。

常用工具

  1. DB Browser for SQLite:可视化GUI,打开db文件浏览执行SQL,跨平台免费。
  2. sqlite3 cli:命令行工具。

常见坑

  1. 外键写了但是没生效 → 忘记执行 PRAGMA foreign_keys=ON
  2. 批量插入很慢 → 没有手动开启事务。
  3. 并发多线程报错:cannot send Connection between threads → 不要共享Connection,使用连接池。
  4. 复制db备份后数据不全 → WAL模式只复制db文件,没复制wal日志。
  5. 更新大量数据后文件体积不缩小:删除数据不会释放磁盘空间,需要执行 VACUUM