SQLite 数据库变大就要迁移吗?一次“代码已升级、索引仍停旧版”的容量复盘

 1个月前     901  

文章目录

SQLite 数据库越来越大时,人最容易盯住的是文件大小:十几 GB、几十 GB,接下来是不是就会变慢、损坏,或者必须迁移到 PostgreSQL?

最近我给一个长期运行的个人系统做容量体检,也遇到了这个问题。现场并没有出现查询失效,磁盘也还有余量,但数据库增长很快。继续只看文件总量,会得到两个同样草率的结论:要么“SQLite 上限很高,不用管”,要么“文件太大了,赶紧换数据库”。

真正有用的判断不是“多大算大”,而是回答四个问题:什么在增长、增长是否仍有价值、当前读写是否健康、出现故障后多久能恢复。

文件大小是报警器,不是诊断结果

从 SQLite 的实现上限看,普通的几十 GB 距硬上限还很远。SQLite 官方文档写明,在默认 4096 字节页面大小下,默认最大数据库约为 17.5 TB。[1]

但这不等于几十 GB 一定安全。硬上限只回答“格式还能不能装下”,不回答:

  • 磁盘还剩多少可增长空间;
  • 备份、复制、校验和恢复需要多久;
  • 查询是否开始扫描更多页面;
  • 写事务是否被长查询拖住;
  • 全文索引、二级索引和重复载荷是否正在放大增长;
  • 当前运行模式能否承受更长的维护窗口。

所以,我把文件大小当作启动调查的阈值,而不把它直接当作迁移阈值。

第一步:把“一个大文件”拆成四类占用

一次有效的容量审计,至少要分开看四类对象。

1. 业务载荷

先确认最大的表存的是什么:正文、历史事件、工具结果、二进制内容,还是可以重建的缓存。高增长未必是浪费;如果数据确实被检索、审计或恢复使用,它就是业务成本。

反过来,如果大量内容永远不会再被读取,继续保留只是把“舍不得删”伪装成数据治理。

2. 二级索引与全文索引

同一段正文进入主表后,还可能进入普通索引、全文索引、词项表和位置表。原始载荷增长一份,落盘占用却可能增长数份。

我这次排查里,最值得处理的不是主表本身,而是一个运行代码已经支持更紧凑布局、现有数据库却仍保留旧索引结构的问题。更新程序只改变了以后“应该怎样建”,没有自动把既有持久数据改成新结构。

这也是最容易被忽略的一层:代码版本已升级,不等于数据库布局已经迁移。

3. 空闲页面

删除记录后,数据库文件不一定缩小。SQLite 在未启用相应自动收缩模式时,会把不用的页面放入 freelist(空闲页列表),供后续写入复用,文件尺寸可以保持不变。[2]

因此,要同时查看页面大小、总页面数和空闲页面数,例如:

PRAGMA page_size;
PRAGMA page_count;
PRAGMA freelist_count;

freelist_count 返回数据库文件中的未使用页面数。[2] 只有“空闲页很多”成立时,讨论回收空间才有意义;如果页面仍被有效数据和索引占用,直接压缩不会凭空消除业务载荷。

4. WAL 与临时文件

使用 WAL(Write-Ahead Logging,预写日志)时,主数据库之外还要看 -wal 与 -shm。WAL 短时增长不一定是故障,但长期读事务可能阻止 checkpoint(检查点)继续推进。[3]

这就解释了为什么“主库没怎么变,磁盘却突然吃紧”有时不是索引膨胀,而是日志、检查点或长事务问题。主库、WAL、临时文件必须分开量,不能只报一个目录总数。

第二步:测真实使用,而不是只跑完整性命令

容量风险最终要落到用户可感知行为。我更关注下面五组结果:

  1. 高频查询与中文/全文搜索的实际延迟;
  2. 写入是否出现排队、锁等待或明显抖动;
  3. 一段固定观察窗内,主库、WAL、索引分别增长多少;
  4. 备份能否在可接受时间内完成;
  5. 恢复一份同量级副本需要多久、需要多少额外磁盘。

这次检查中,代表性搜索仍保持在毫秒级,WAL 也没有持续失控。这个结果说明当前不是“数据库已经不可用”的事故,但不能据此推导未来增长到任意规模都没问题。

最重要的未验证项反而是恢复时间。生产系统真正的容量边界,常常不是能否继续写入,而是出了问题还能不能在业务允许的窗口内恢复。

SQLite 的在线备份 API 可以增量复制正在使用的数据库,并只在读取页面的短时段持锁,让其他用户继续工作。[5] 对长期运行的系统,这比随手复制一个正在写入的数据库文件更适合作为受控恢复点。

第三步:按收益排序治理动作

我不会从 VACUUM 开始。更稳妥的次序是:

1. 先停掉没有价值的新增放大

如果新代码已经收窄索引范围,而旧库仍停在老布局,先做显式、可验证的索引迁移。迁移后观察新的写入放大,再讨论回收历史空间。

2. 再处理保留策略

区分三种数据:

  • 必须长期保留的权威记录;
  • 可以归档到冷存储、但偶尔要找回的历史;
  • 可重建、可过期或重复的派生数据。

不要为了让文件变小,直接删除仍有唯一恢复价值的历史;也不要因为“以后可能有用”,让所有中间结果永久在线。

3. 最后才决定是否重建文件

VACUUM 会重建数据库并压实空闲空间,但官方文档提醒,它执行时可能需要多达原数据库两倍的额外可用磁盘。[4]

这意味着大库上的 VACUUM 不是一个随手优化命令,而是需要恢复点、独占条件、容量预算和中断窗口的写操作。若空闲页很少,它的收益也可能不值得承担这次维护。

什么时候才该迁移到客户端/服务器数据库

真正推动迁移的,通常不是某个整数 GB,而是工作负载改变:

  • 多个进程或多台机器需要直接并发写同一个库;
  • 写入竞争开始影响业务时延;
  • 数据必须跨网络由多个客户端共享;
  • 备份、恢复、复制和高可用已经超出单文件运维能力;
  • 数据规模、查询模型或权限边界要求分片、只读副本或独立数据库服务。

SQLite 官方给出的边界也很直接:它可以同时服务很多读者,但任一时刻只允许一个写者;高写并发的系统可能需要别的方案。[8]

反过来,若数据库与应用仍在同一台机器、写入可串行、查询稳定、恢复时间可控,那么仅仅因为文件“看起来很大”就迁移,可能只会换来更多组件、网络故障面和维护成本。

结论

SQLite 数据库变大后,正确动作不是先问“它能不能扛到某个数字”,而是完成一张可验证的容量账:

  1. 主表、索引、全文索引、空闲页和 WAL 各占多少;
  2. 哪些增长有价值,哪些是旧布局或重复载荷造成的放大;
  3. 代表性读写是否仍健康;
  4. 备份与恢复能否落在允许窗口;
  5. 当前真正受限的是文件大小、磁盘、写并发,还是维护能力。

这次复盘最反直觉的发现是:程序已经升级,不代表旧数据库已经享受新存储策略。先确认持久布局有没有真正迁移,再谈压缩、清理或换库,往往比盯着文件大小做决定更可靠。

本文是 AI 辅助整理的原创工程实践内容,现场数据和系统身份已做匿名化与简化。

来源与核验

  1. SQLite 官方:Implementation Limits For SQLite(核验于 2026-09-05)
  2. SQLite 官方:PRAGMA statements(核验于 2026-09-05)
  3. SQLite 官方:Write-Ahead Logging(核验于 2026-09-05)
  4. SQLite 官方:Backup API(核验于 2026-09-05)
  5. SQLite 官方:VACUUM(核验于 2026-09-05)
  6. SQLite 官方:Appropriate Uses For SQLite(核验于 2026-09-05)

暂无评论

暂无评论...