本节摘要:SQLite 的诊断工具集中在一个命令行工具与一组 PRAGMA 里。本节给出一条完整的排障路径:从"感觉慢"出发,先查库健康(integrity_check 家族与空间统计),再查计划(EXPLAIN 与 .eqp),再查执行(.timer 与状态计数),最后对照 MySQL 与 PostgreSQL 的诊断入口,形成可复用的排障流程。
拿到任何"行为异常"的库,先跑完整性家族:
PRAGMA integrity_check; -- 全量校验:逐页核对 B-Tree 结构、单元格边界、索引一致性 PRAGMA quick_check; -- 快速版:跳过部分索引内容核对,大库先跑它 PRAGMA foreign_key_check; -- 外键违例清单(开了外键约束的库)
integrity_check 输出 ok 即健康;否则列出每个损坏页的报告。三个命令各有分工:怀疑断电损坏跑全量;日常巡检用 quick;数据"莫名消失"先查外键违例再看业务逻辑。空间健康是第二类体检:PRAGMA freelist_count 看空闲页数量——freelist 占比超过两三成说明删除频繁且从未 VACUUM,此时文件虚胖、扫描路径变长;PRAGMA page_count 加页大小算出实际占用;dbstat 虚表(第 2 章用过)能下钻到每张表每棵索引的页数分布,找出"谁占着空间"。
-- 空间体检组合 PRAGMA page_count; -- 总页数 PRAGMA freelist_count; -- 空闲页数,占比高即碎片化 SELECT name, SUM(pgsize)/1024 AS kb FROM dbstat GROUP BY name ORDER BY kb DESC LIMIT 10; -- 空间占用前十的对象
慢查询的标准流程在第 5、6 章已建立,这里收拢成 CLI 操作序列:
sqlite3 app.db .mode column .headers on .timer on -- 打开真实耗时 .eqp on -- 每条语句自动先显示查询计划 EXPLAIN QUERY PLAN SELECT ... ; -- 计划与行号 SELECT ... ; -- .timer 给出 real 时间 .stats on -- 语句级的缓存命中、排序溢出等计数
.stats on 的输出值得专门一课:它展示当前会话的页缓存命中与未命中次数、排序器使用情况、语句编译次数。把"慢"归因到这三类——缓存未命中多去查 7.1 的预算,编译次数多去查 7.3 的语句复用,排序溢出多去查 temp_store 与索引消除。对照另外两家的入口:MySQL 的 EXPLAIN ANALYZE(8.0 新增)给出真实执行行数,PostgreSQL 的 EXPLAIN (ANALYZE, BUFFERS) 直接报每节点的页命中与读盘——三库里只有 PG 能把"计划假设对不对"与"页从哪来"一次看全;SQLite 用 .stats 与 .timer 的组合近似补齐。
一份合格的性能工单包含五件事,全部可用 SQLite 自身工具产出:
| 证据项 | 采集命令 | 看什么 |
|---|---|---|
| 库版本与配置 | SELECT sqlite_version(); 加 PRAGMA 组合 |
版本差异可能就是问题本身 |
| 库健康 | integrity_check 加 freelist_count | 排除损坏与碎片 |
| 空间分布 | dbstat 聚合 | 大表大索引是谁 |
| 计划 | EXPLAIN QUERY PLAN | SCAN 还是 SEARCH、有无 TEMP B-TREE |
| 耗时分解 | .timer 加 .stats | 真实时间加缓存命中率 |
五项齐全后再谈改法——多数"SQLite 太慢"的工单走到第三站就真相大白:不是引擎慢,是 freelist 占了 40%、或查询在无索引的表上全扫、或循环里每次重新 prepare。
💡 关键直觉:诊断的本质是把"感觉"替换成"计数器"。SQLite 的计数器都是 PRAGMA 与状态查询的形态,随手可得;先采数再下结论的习惯,比任何单个工具都值钱。
MySQL 的诊断体系重"服务视角":SHOW PROCESSLIST 看谁在跑、慢查询日志按时间阈值捕获、performance_schema 逐阶段计时。PostgreSQL 重"统计视角":pg_stat_statements 把每条语句的累计耗时与调用数排成榜、auto_explain 自动捕获慢计划。SQLite 没有"服务",自然没有这两类设施——它的对等物是应用内的 profiling:包一层 step 计时、定期采样 sqlite3_status。做混合架构的团队值得把三套诊断词汇做成对照表,故障时才知道每个系统该问哪个问题。
**数据库多大需要"拆"?**SQLite 没有技术上的大小红线,数百 GB 的单库在官方邮件列表里屡见不鲜。真正该拆的信号是三个运维量:VACUUM 或备份的窗口超出可接受时长(线性于库大小);单文件达到所在文件系统或同步盘的规格边界;业务上数据天然按用户或租户分片(此时一租户一库反而更符合 SQLite 的形态)。相反,"库大了会慢"的说法要拆开看——B-Tree 层数只是对数增长,慢与不慢看索引与缓存,不看绝对大小。
**wal_checkpoint 返回的那行怎么解读?**四个数字依次是:忙标记(1 表示本次未能完成全部工作)、WAL 里的总页数、本次成功回写主文件的页数、检查点推进后整个文件可安全重用的页数。诊断口诀:第二列长期大于第四列说明 WAL 在堆积;忙列为 1 且第三列很小,去应用里找长命读事务。配合 4.3 节的四个病因清单,WAL 膨胀问题基本都能在五分钟内定位。
**慢查询日志有对应物吗?**SQLite 没有内置的慢日志——没有服务进程就没有集中的观测点。替代方案在应用侧:给语句执行包一层计时钩子(C API 的 trace 机制或语言绑定的回调),超过阈值的语句连同计划快照落进应用日志;再配合定期抽样的 sqlite3_status 峰值数据。搭好后你会得到与慢查询日志等价的信息,且过滤维度完全自定义——这是没有服务层的自由,也是代价。
数据保住了、健康查清了,下一节处理最后一道防线:安全。