SQLite是一个基于文件的内嵌式数据库,它的所有数据都以固定大小的页(page)为单位组织存储。每个数据库文件从逻辑上被切分成若干个页,B树节点、索引节点、溢出数据全部落在这些页上。而PRAGMA page_size就是用来设定这个页大小的开关。看似只是一个数字,但它对数据库的读写性能、存储空间利用率甚至单行数据上限都有直接影响,而且它有一个非常特殊的规则:只有在数据库真正写入数据之前设定才有效,很多初学者在这里栽了跟头。

page_size的工作原理与底层机制
SQLite采用页式存储结构,默认页大小是4096字节(4KB)。数据库文件被划分为一个个连续的页,每一页要么是一个表B树的叶子页或内部页,要么是索引页、空闲页、或者页头部。每次读写操作的最小单位就是一个页,也就是说哪怕你只更新了一条记录中的某一个字段,SQLite也要把这个字段所在的整页读入内存,修改后再整体写回。
页大小决定了三个关键指标。第一是I/O粒度,page_size越大,单次磁盘操作搬运的数据越多,顺序扫描大表时吞吐量更高;第二是B树扇出,页越大,每个节点能容纳的键值越多,树的高度越低,范围查询需要访问的节点数就越少;第三是缓存粒度,SQLite的页面缓存以页为单位管理,缓存总字节数固定的情况下,页越小,缓存能覆盖的页数越多,随机访问小记录的场景反而更划算。
需要注意,page_size必须是2的幂,合法取值为512、1024、2048、4096、8192、16384、32768、65536字节。超出这个范围的值会被忽略并保持原值不变。另外单行记录(不含BLOB和长文本)的大小不能超过可用页空间的四分之一左右,极端情况下把page_size设成512可能导致稍宽的表无法建表成功。
为什么必须在建库之前设定page_size
这是page_size最容易踩坑的地方。SQLite的页大小写进数据库文件头部(文件前100字节中的偏移16处),一旦文件创建完成,页大小就固定了。PRAGMA page_size = 8192;这样的语句如果在一个已经有数据的库上执行,表面上不会报错,命令也会返回成功,但实际页大小纹丝不动,这种“静默失败”让很多开发者误以为配置生效了。
正确的做法有两种。第一种是全新数据库流程:先执行PRAGMA page_size,再做任何写操作(包括建表),SQLite在首次分配页时会用新值初始化文件头。第二种是已有数据的库想改变页大小,需要执行PRAGMA page_size = 新值;之后紧跟着执行VACUUM;,VACUUM会重建整个数据库文件,重建过程中使用当前设定的page_size,从而实现页大小的变更。示例流程如下:
-- 全新数据库:先设页大小再建表
PRAGMA page_size = 8192;
CREATE TABLE user (
id INTEGER PRIMARY KEY,
name TEXT,
score REAL
);
-- 已有数据库:设置后必须配合VACUUM重建文件
PRAGMA page_size = 16384;
VACUUM; -- 重建文件,新的页大小在此时生效
-- 查看当前页大小
PRAGMA page_size; -- 返回 16384
执行完之后可以用PRAGMA page_size;(不带等号)回查确认。如果返回值还是4096,说明设置没有生效,大概率是数据库已有数据但没有执行VACUUM,或者设置动作发生在文件创建之后。另外注意,如果开启了WAL模式,改页大小需要先用PRAGMA journal_mode = DELETE;退出WAL,改完再切回去。
不同取值的性能取舍与选型建议
page_size没有万能的最优值,选择取决于负载类型。写入密集型的场景(大量的INSERT和UPDATE)倾向于较小的页,比如1024或2048,因为每次写回的数据量小,事务日志页也小,能减少写放大,尤其适合嵌入式设备上的闪存存储。读取密集型、尤其是全表扫描和范围查询为主的场景,倾向于较大的页,8192到65536都能带来可观的提升,树高降低带来的寻路成本节约非常明显。
另一个常被忽略的关联配置是PRAGMA cache_size。cache_size的单位默认是页数(负数时表示字节数的KiB值)。假如cache_size保持默认的2000页不变,把page_size从4096改成65536,缓存总量会从约8MB膨胀到125MB,可能直接把内存吃爆。所以调整page_size时一定要同步评估cache_size,建议用负数形式显式指定缓存字节数,例如PRAGMA cache_size = -65536;表示最多使用64MB缓存。
下面是一个用Python创建数据库并设置页大小的完整例子,涵盖了几项常用配套参数:
import sqlite3
# 全新数据库文件,先配置页大小再执行DDL
conn = sqlite3.connect("app.db")
cur = conn.cursor()
cur.execute("PRAGMA page_size = 8192") # 页大小设为8KB,必须在建表之前
cur.execute("PRAGMA cache_size = -65536") # 页面缓存上限64MB(负数表示KiB)
cur.execute("PRAGMA journal_mode = WAL") # 写前日志模式,提升并发读写性能
cur.execute("PRAGMA synchronous = NORMAL") # WAL下NORMAL已足够安全
cur.execute("PRAGMA page_size") # 回查确认
print("当前页大小:", cur.fetchone()[0]) # 输出 8192
cur.execute("""
CREATE TABLE log (
id INTEGER PRIMARY KEY,
ts INTEGER,
level TEXT,
msg TEXT
)
""")
conn.commit()
conn.close()
如果使用C接口,顺序同样关键:先sqlite3_open打开(或创建)空文件,立即执行sqlite3_exec(db, "PRAGMA page_size = 8192;", ...),然后才执行CREATE TABLE。只要第一张表建好、第一条数据写入,页大小就永久固定了。做压力测试时建议用sqlite3_analyzer工具分析页利用率,如果发现大量溢出页(overflow page),说明记录平均长度超过页容量的比例偏高,适当增大page_size或改用独立的BLOB表会更合适。综合来看,通用桌面或服务端应用选8192是个稳妥起点,移动端小数据量场景保持默认4096即可,只有在明确测量出收益后再去动这个参数。
SQLitePRAGMA page_size页面大小修改时间:2026-09-07 09:12:42