SQLite 详细介绍
SQLite 是零配置、文件型、嵌入式关系数据库,C语言开发,体积很小。 它不是客户端‑服务端数据库(MySQL/PostgreSQL是C/S架构),数据库就是一个普通磁盘文件,直接由程序读写这个文件,没有独立后台服务进程。
一句话:把数据库库链接进你的程序,程序直接读写
.db文件。
核心特点
✅ 优点
- 无服务,零安装 没有守护进程,不需要启动服务,不需要账号密码。数据库就是单个文件,复制、备份、拷贝直接复制db文件即可;也支持内存数据库
:memory:,程序结束数据销毁。 - 嵌入式 库文件很小,静态编译后可以打包进应用。手机APP、浏览器、桌面软件、嵌入式设备大量使用。Rust、Python、Go都有成熟绑定。
- 完整SQL支持 支持标准SQL:表、索引、事务、JOIN、视图、触发器、外键、子查询。
默认外键约束默认关闭,需要手动开启。
- 单文件 整个数据库全部内容(表、索引、元数据)存放在一个磁盘文件。备份极其简单,复制文件。
- 事务ACID 完全支持ACID事务,崩溃安全。断电、程序异常不会损坏数据库文件。
- 跨平台 同一个
.db文件,Windows / Linux / macOS / Android 直接通用。
⚠️ 短板(非常关键,选型重点)
- 全局写锁
SQLite 同一时刻只能有一个写事务;可以多个并发读。 多个线程/进程同时写,会排队;高并发写场景性能差,不适合高并发写业务。 读‑写会互斥,写的时候所有读会被阻塞。
- 不适合网络多客户端访问 不适合多台机器同时操作同一个db文件;文件锁在网络共享盘(NFS/SMB)不可靠。 适合:单服务进程访问;不适合:多实例微服务同时读写同一个db。
- 并发能力弱于 MySQL / PostgreSQL 读性能很好,写是瓶颈。大批量插入必须用事务,否则极慢。
- 数据库文件上限 理论最大文件 ~140TB,实际受操作系统限制。单表亿级数据可以跑,但索引设计很重要。
- 用户权限模型弱 没有MySQL那种用户、grant权限体系,权限完全靠操作系统文件权限控制db文件。
SQLite 存储模型
文件结构
- 主数据库文件:
xxx.db,全部数据 xxx.db‑wal:WAL模式日志文件(Write‑Ahead‑Log)xxx.db‑shm:WAL共享内存文件
WAL模式是现代SQLite默认模式,大幅提升并发读,写的时候读不受阻塞。
两种日志模式
- DELETE 旧模式 写操作直接覆盖原文件,写时阻塞读,现在很少用。
- WAL(预写日志)默认 修改先写入wal日志,不修改主db文件;读从主文件读取。
- 写不会阻塞读,读不会阻塞写(但仍然只能一个写)
- 定期 checkpoint 将wal合并入主db。>
开启WAL会多出两个附属文件,备份需要把三个文件一起复制。
数据类型(SQLite是动态类型,这点和别的数据库差异巨大)
SQLite 是弱类型数据库,列声明类型只是“建议”,可以存任意类型的值。 内部只有5种存储类:
NULLINTEGER有符号整数 1‑8字节REAL浮点数TEXTUTF‑8字符串BLOB原始二进制字节
CREATE TABLE t(age INT);
INSERT INTO t(age) VALUES ("abc"); -- 可以插入字符串到INT列!不会报错开发注意:业务层(Rust代码)做类型校验,不要完全依赖数据库约束。
常用类型映射 Rust <-> SQLite
| SQLite | Rust |
|---|---|
| INTEGER | i64 / i32 |
| TEXT | String |
| REAL | f64 |
| BLOB | Vec |
| NULL | Option |
重要开关 / 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 适合什么场景
✅ 非常适合
- 桌面客户端APP、GUI程序
- 移动端APP
- 小型后端服务,单实例,读多写少
- 本地缓存、离线数据存储
- 单元测试内存数据库
:memory: - 嵌入式设备
- 中小型数据量,单进程访问
❌ 不适合
- 多台服务器同时读写同一个db文件
- 极高并发写业务(每秒大量写入)
- 微服务多实例共享同一个数据库
Rust rusqlite 使用要点总结
Connection不是 Send,不能跨线程。多线程必须用连接池r2d2‑rusqlite。- 打开连接第一件事执行
PRAGMA foreign_keys=ON;。 - 大批量修改,务必包裹事务。
- 禁止字符串拼接SQL,永远使用参数绑定
?1 ?2,防止注入。 - SQLite写锁全局,连接池不要设置过大,一般4‑8足够。
- NULL字段 Rust 必须用
Option<T>。 - WAL模式下备份要拷贝
.db.db‑wal.db‑shm三个文件;或者执行VACUUM INTO 'backup.db'做在线备份。
常用工具
- DB Browser for SQLite:可视化GUI,打开db文件浏览执行SQL,跨平台免费。
- sqlite3 cli:命令行工具。
常见坑
- 外键写了但是没生效 → 忘记执行
PRAGMA foreign_keys=ON。 - 批量插入很慢 → 没有手动开启事务。
- 并发多线程报错:cannot send Connection between threads → 不要共享Connection,使用连接池。
- 复制db备份后数据不全 → WAL模式只复制db文件,没复制wal日志。
- 更新大量数据后文件体积不缩小:删除数据不会释放磁盘空间,需要执行
VACUUM。