VACUUM

1. 背景:为什么需要 VACUUM?

在 SQLite 的日常使用中,你会发现一个奇怪的现象:当你从数据库中删除了大量数据后,数据库文件的大小(在磁盘占用的空间)并没有变小。

这是因为 SQLite 的默认行为:

  1. 当你执行 DELETE 删除数据时,SQLite 只是将这些数据占用的页面标记为“空闲”(Free Pages)。
  2. 这些空闲页面保留在数据库文件中,等待将来插入新数据时重复使用。
  3. 不会主动将这部分空间归还给操作系统。

这种机制可以提高性能(避免频繁的文件截断和扩展操作),但会导致数据库文件包含大量的“气泡”(碎片),导致磁盘空间浪费,且数据分布分散可能影响查询速度。

VACUUM 命令就是用来解决这个问题的。

2. VACUUM 的作用

VACUUM 命令主要执行两个操作:

  1. 重建数据库文件:它会创建一个全新的数据库文件,将原有数据库中的有效数据紧凑地复制过去,丢弃所有的空闲页面。
  2. 最小化文件体积:操作完成后,新的数据库文件只包含实际数据,体积会变小,释放出的磁盘空间归还给操作系统。

可以将它类比为电脑硬盘的“磁盘碎片整理”。

3. 基本用法

语法非常简单,通常不需要任何参数:

VACUUM;

你也可以对附加(Attached)的数据库执行此操作:

VACUUM "secondary_db";

4. 执行过程与内部原理

理解 VACUUM 的内部工作原理对于评估其风险至关重要:

  1. 创建临时文件 :SQLite 在同一目录下创建一个临时的数据库文件。
  2. 复制数据 :从源数据库读取有效数据,重新整理并按顺序写入临时文件。此时会忽略所有的空闲页面。
  3. 替换文件 :复制完成后,SQLite 删除源文件,并将临时文件重命名为源文件名。
  4. 重新加载 :重新打开新的数据库文件。

5. 重要注意事项与风险

在生产环境中使用 VACUUM 前,必须了解以下限制:

5.1 磁盘空间峰值

由于 VACUUM 会创建一个完整的副本,在执行过程中,你需要至少拥有相当于原数据库文件大小两倍的剩余磁盘空间

  • 如果你的数据库是 10GB,且磁盘剩余空间只有 5GB,执行 VACUUM 会失败。

5.2 独占锁(Exclusive Lock)

VACUUM 操作需要独占访问权。

  • 在执行期间,数据库被完全锁定。
  • 任何其他的读取或写入操作都无法进行,直到 VACUUM 完成。
  • 对于大型数据库,这可能意味着几秒甚至几分钟的停机时间。

5.3 ROWID 的变化

如果你的表中没有显式定义 INTEGER PRIMARY KEY,表使用的是系统生成的隐式 ROWIDVACUUM 重建表时,可能会重置这些隐式的 ROWID

  • 建议:总是显式定义主键,以避免此问题。

6. VACUUM INTO (备份功能)

从 SQLite 3.27.0 (2019年) 开始,引入了 VACUUM INTO 语法。

这是一个生成数据库备份的绝佳方式。与直接复制文件不同,VACUUM INTO 是事务安全的,可以在数据库被应用程序使用的同时执行。

-- 将整理后的数据库副本保存为 backup.db
VACUUM INTO 'backup.db';

生成的 backup.db 是一个经过压缩整理的、最小化的数据库副本,非常适合作为备份文件传输。

7. 自动清理 (Auto Vacuum)

除了手动执行 VACUUM,SQLite 还提供了 auto_vacuum 模式,通过 PRAGMA 设置。

查看当前模式:

PRAGMA auto_vacuum;
  • 0NONE (默认):不自动回收空间。
  • 1FULL:自动回收。
  • 2INCREMENTAL:增量回收。

为什么通常不推荐 FULL 模式?

虽然 PRAGMA auto_vacuum = FULL 可以让 SQLite 在每次事务提交后自动裁剪文件大小,但这会造成严重的碎片化性能损耗。因为它必须频繁地移动页面来填补空白,导致写入速度变慢。

推荐策略

对于大多数应用:

  1. 保持默认设置 (auto_vacuum = NONE)。
  2. 在维护窗口期(例如每天深夜或每周一次)手动执行 VACUUM
  3. 或者仅在检测到空闲页面比例过高时执行(通过 PRAGMA freelist_count 查询)。

8. 总结

  • 用途:回收删除数据后的磁盘空间,整理碎片,减小文件体积。
  • 代价:需要双倍磁盘空间,且会长时间锁定数据库。
  • 最佳实践:不要频繁运行。通常在大量删除操作(如清理旧日志)之后,或在定期维护任务中运行。
  • 高级技巧:使用 VACUUM INTO 进行安全、紧凑的备份。