Bolt 是一个优秀、纯 Go 实现、支持 ACID 事务的嵌入式 Key/Value 数据库。但在使用过程中会有很多空间碎片。一般数据库占用的空间是元数据空间的 1.5~4 倍。Bolt 没有内置的压缩功能,需要手动压缩。
ACID,是指数据库管理系统(DBMS)在写入或更新资料的过程中,为保证事务(transaction)是正确可靠的,所必须具备的四个特性:原子性(atomicity,或称不可分割性)、一致性(consistency)、隔离性(isolation,又称独立性)、持久性(durability)。
产生碎片原因
当插入键/值对时,Bolt 需要找到一组连续的空闲页面,或者需要在文件末尾添加更多页面。 当值很小(小于 4KB)时,很容易找到连续的页面,因为 Bolt 只需要找到一个空闲页面。 随着键值的值变大,很难找到“完美”大小的空闲页面集,因此它会追加到末尾。键值对越大,越容易出现严重的碎片,当不断对数据库做删除时,碎片会更多。
碎片整理过程
假设当前数据库是 a.db
,压缩后的数据库是 b.db
- 打开数据库文件
a.db
和b.db
。 - 从
a.db
数据库读取每个对象,并将其写入b.db
- 关闭两个文件。
- 重命名。 把
b.db
重命名为a.db
。操作系统,重命名是原子的。 - 重新打开新压缩的
a.db
文件
这过程用到 Bolt
两个有用的遍历函数: tx.ForEach
、bucket.ForEach
第一个是遍历所有的 Bucket
1
2
3
4
5
6
7
8
9
10
11
// ForEach executes a function for each bucket in the root.
// If the provided function returns an error then the iteration is stopped and
// the error is returned to the caller.
func (tx *Tx) ForEach(fn func(name []byte, b *Bucket) error) error {
return tx.root.ForEach(func(k, v []byte) error {
if err := fn(k, tx.root.Bucket(k)); err != nil {
return err
}
return nil
})
}
第二个是遍历某个 Bucket
里所有的 Key/Value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ForEach executes a function for each key/value pair in a bucket.
// If the provided function returns an error then the iteration is stopped and
// the error is returned to the caller. The provided function must not modify
// the bucket; this will result in undefined behavior.
func (b *Bucket) ForEach(fn func(k, v []byte) error) error {
if b.tx.db == nil {
return ErrTxClosed
}
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
if err := fn(k, v); err != nil {
return err
}
}
return nil
}
如果在数据库里使用嵌套 Bucket
,还需处理一下
参考
本文网址: https://golangnote.com/topic/311.html 转摘请注明来源