PRAGMA
本教程共 50 篇 · 第 41 篇 · 更新于 2026-07-31
41. PRAGMA
本节目标:学完本章你能用 PRAGMA 读取数据库元信息(表结构、索引、编码等)并调整运行时行为(外键开关、缓存大小、同步模式等)。
PRAGMA 是 SQLite 特有的”特殊命令”,用来读取或设置数据库的各种环境变量与状态标志。它既不是标准 SQL 的表,也不是普通函数,而是 SQLite 留给你的”控制面板”。回想 SQLite 的出身:serverless、零配置,整个数据库就是单个文件,没有常驻服务进程帮你管状态。所以很多”该不该开外键""缓存多大""怎么同步落盘”这类开关,就通过 PRAGMA 在连接层面直接设定。
PRAGMA 有两种用法:只写名字是”读”,写 名字 = 值 是”写”。
PRAGMA foreign_keys; -- 读当前外键开关
PRAGMA foreign_keys = ON; -- 打开外键强制
常见坑(外键默认关闭)
SQLite 默认不强制外键约束! 即使你建表时写了
REFERENCES,只要没显式打开,插入违反参照完整性的数据也不会报错。而且PRAGMA foreign_keys = ON是连接级别的设置,不是写进数据库文件的——每一次新连接(包括每次重新打开 CLI)都得重新执行,它不会”记住”。这是新手最常踩的坑:在 A 连接开了外键、在 B 连接插入脏数据,结果 B 完全没拦。官方在 foreignkeys 文档里也明确要求由应用每次连接时设置。
一、最常用的几个 PRAGMA
foreign_keys:外键强制开关,上文已强调。涉及 orders.user_id 引用 users.id 这类关联时务必先开。
cache_size:设置内存页缓存的页数(默认 2000 页,最小 10 页)。注意它是”临时”的,只对当前连接有效,且是按页算不是按字节。
PRAGMA cache_size = 10000;
journal_mode:事务日志模式,影响并发与崩溃安全。常用值:DELETE(默认,事务结束删日志)、WAL(预写日志,读写并发更好)。WAL 模式在嵌入式/高读场景很受欢迎。
PRAGMA journal_mode = WAL;
synchronous:控制数据写入物理存储的激进程度。OFF(0) 不同步、NORMAL(1) 关键序列后同步、FULL(2) 每次关键操作都同步。FULL 最安全但最慢,OFF 最快但掉电可能损坏。
encoding:数据库字符串编码(UTF-8 / UTF-16le / UTF-16be),通常在建库时定好。
user_version / schema_version:存在数据库头里的 32 位整数。user_version 完全由开发者自由使用(比如做”数据库 schema 版本号”),schema_version 由 SQLite 在每次改结构时自增,一般别手动改。
二、读取元信息的 PRAGMA
PRAGMA 也是”查看数据库内部结构”的好工具,比翻 sqlite_master 更省事:
| PRAGMA | 作用 |
|---|---|
table_info(表名) | 查看某表的列:序号、列名、类型、可否为 NULL、默认值、是否主键 |
index_list(表名) | 列出与表相关的所有索引 |
index_info(索引名) | 查看某索引包含哪些列 |
foreign_key_list(表名) | 查看表上的外键定义 |
database_list | 列出当前所有已打开/附加的数据库 |
page_count / page_size | 当前页数 / 页大小(乘积约为文件字节数) |
freelist_count | 当前被标记为空闲可复用的页数 |
PRAGMA table_info(users);
PRAGMA index_list(orders);
举个完整例子:假设 orders(user_id) 引用了 users(id),想确认这张表到底挂了哪些外键,用 foreign_key_list:
PRAGMA foreign_key_list(orders);
-- 返回:id, seq, table(被引用表), from(本表列), to(被引用列), on_update, on_delete, match
database_list 在多库/附加(ATTACH)场景下尤其有用,能一次看清当前连接里挂了哪几个库、分别指向什么文件——对 SQLite 这种”单文件即数据库、可同时 ATTACH 多个文件”的模型来说,是理清连接拓扑的利器。
重点提示
journal_mode = WAL是实践中最常被推荐的优化之一。DELETE 模式下写操作会阻塞读;WAL(Write-Ahead Logging)模式下,读和写可以真正并发,读写性能都更好,特别适合”读多写少”的嵌入式/单机应用。代价是会产生-wal和-shm两个附属文件,备份时要一并带上或先PRAGMA wal_checkpoint。
重点提示
许多只读、无副作用的 PRAGMA(如
table_info、index_list)还能当成”表值函数”来用,也就是可以出现在SELECT ... FROM pragma_table_info('users')里,方便和其他表 JOIN 或过滤。这个特性从 3.16.0 起支持。带副作用或要传参设置的 PRAGMA 则不能这么用。
三、其他值得了解的设置
auto_vacuum:控制删除数据后数据库文件是否自动收缩(0 关闭 / 1 全自收缩 / 2 增量,需手动 incremental_vacuum)。默认关闭,文件不会自动变小,需用 VACUUM 命令整理(详见维护章)。
recursive_triggers:是否允许触发器连锁触发另一个触发器,默认关闭。
case_sensitive_like:控制 LIKE 是否区分大小写,默认不区分。
temp_store:临时表/临时数据放文件还是内存(0 默认 / 1 文件 / 2 内存)。在内存充裕、追求临时排序/分组速度的场景可设成 2,但要注意内存总量,别让大查询把内存吃满。
实用技巧
排查”为什么这张表查得慢”时,一个实用组合是:
PRAGMA table_info(表名)看字段、PRAGMA index_list(表名)看有没有索引。再配合后续的EXPLAIN QUERY PLAN,就能快速定位是不是全表扫描。把 PRAGMA 当成你的”数据库体检仪”。
四、类比小结
把 PRAGMA 想成汽车的”仪表盘和设置旋钮”:仪表盘那部分(table_info、index_list、page_count)让你看清车子当前内部状况;设置旋钮那部分(foreign_keys、cache_size、journal_mode、synchronous)让你按行驶环境调参。重要的是记住——很多旋钮(尤其 foreign_keys)是”人上车就要重新拧”的,不会因为上次拧过就永久记住。这也提醒我们:PRAGMA 的”写”动作不等于”改了数据库文件”,它更像改了这次驾驶的偏好,下次换人开车(新连接)依然回到默认设置。
一句话适用场景:想知道数据库”长什么样""有哪些索引""编码和版本是多少”,用读类 PRAGMA;想调并发、安全、性能、外键等行为,用写类 PRAGMA。其中外键开关务必每次连接都开,这是红线级的注意事项。
常见坑(收尾提醒)
PRAGMA 不是标准 SQL,可移植性差:你写在脚本里的
PRAGMA foreign_keys = ON在 MySQL/PostgreSQL 里完全没有对应物,跨库迁移不要指望它能通用。另外,写类 PRAGMA 大多只影响”当前连接”,重启连接后即失效(不像CREATE TABLE那样持久化进文件)。需要持久行为(如默认外键开)应该在应用代码里每次建连后主动设置,而不是依赖某次手动执行。把”该开什么”写进连接初始化逻辑,比靠记忆更可靠。