预分配磁盘空间仅对mmap类数据库(如bbolt)有效,需在open()时通过options.initialmmapsize设置,仅首次创建生效,后续无法调整,须导出重建。

预分配磁盘空间能显著降低高并发写入时的 I/O 毛刺,但仅对 mmap 类数据库(如 bbolt)有效,普通文件写入不适用。
bbolt 的 InitialMmapSize 必须在 Open() 时设置
这个参数只在首次创建数据库文件时生效,后续打开已存在的数据库不会扩容或调整 mmap 区域。如果忘记设置,运行中无法补救 —— 只能导出再重建。
- 必须传给
bbolt.Open()的第三个参数:&bbolt.Options{InitialMmapSize: 1(即 1GB) - 值太小(如默认 0)会导致频繁
mmap扩容,每次触发都可能卡住 goroutine,尤其在高并发写事务时表现明显 - 值太大也不会立即占用磁盘,只是预留虚拟地址空间;但若远超实际数据量,可能影响其他 mmap 应用的地址布局(罕见)
预分配大小要匹配业务写入节奏,不是越大越好
假设你每秒写入 10MB 数据,峰值持续 5 分钟,那至少预留 3GB;但若平均每天只增 100MB,却预设 10GB,既浪费地址空间,又让 bbolt 在 freelist 管理上多做无谓扫描。
- 估算依据:日均写入量 × 保留天数 × 1.5(留冗余)
- 上线前用压测模拟真实写入节奏,观察
db.Stats().FreePageN和db.Stats().TxN变化趋势 - 避免用
os.Truncate()或外部命令“手动预占”,bbolt不识别这种空洞文件,仍会按需 mmap
注意 mmap 与系统虚拟内存限制的冲突
Linux 默认 /proc/sys/vm/max_map_count 是 65530,而一个 bbolt 实例可能占用多个 mmap 区域(尤其开启 Options.MmapFlags 自定义标志时)。预分配过大 + 并发实例多,容易触发 cannot allocate memory 错误,但错误信息里不会提 mmap。
- 检查方式:
cat /proc/$(pidof yourapp)/maps | wc -l - 临时调高:
sudo sysctl -w vm.max_map_count=262144 - 更稳妥的做法:单机部署时限制
bbolt实例数,或改用非 mmap 后端(如pebble)应对极端场景
真正容易被忽略的是:预分配解决的是「扩容抖动」,不是「写入瓶颈」。如果事务逻辑本身慢(比如大量 Bucket.Put() 未批量化),光调 InitialMmapSize 没用 —— 得先看 pprof 的 CPU profile 和 block profile。











