BoltDB
我们现在说的bolt一般都会说是etcd维护的bolt版本,老的bolt作者已经不再维护。bolt提供轻量级的kv数据库,内部是一个B+树结构体,支持完整的ACID事务。 bolt是基于内存映射(mmap)的数据库,通常情况使用的内存会比较大,bolt适合kv数量不是特别大的场景。
创建数据库
package main
import (
"log"
bolt "go.etcd.io/bbolt"
)
func main() {
// 打开my.db 如果文件不存在则会创建
db, err := bolt.Open("my.db", 0600, nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
}
事务
bolt支持只读事务和读写事务, 在同一个时间只能有一个读写事务,但是可以有多个只读事务。
只读事务使用 db.View
err := db.View(func(tx *bolt.Tx) error {
...
return nil
})
读写事务使用 db.Update
err := db.Update(func(tx *bolt.Tx) error {
...
return nil
})
Buckets(桶)
Bolt在内部用Buckets来组织相关的kv键值对,在同一个Bucket里头key值是不允许重复的,bolt中一个文件代表一个database,那么bucket就相当于一个table。
操作Bucket
db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("MyBucket")) //创建bucket,通常会使用 CreateBucketIfNotExists
if err != nil {
return fmt.Errorf("create bucket: %s", err)
}
err := b.Put([]byte("answer"), []byte("42")) //写入kv
v := b.Get([]byte("answer")) //获取value
fmt.Printf("%s",v)
return nil
})
key的遍历
在kv数据库中我们需要对key进行精心设计,无论在取值或者遍历的时候都需要快速的定位key的位置。在bolt中key是基于B树有序的。 一般有如下三种场景遍历key,遍历,范围遍历,前缀遍历
遍历桶中所有key
db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("MyBucket"))
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
fmt.Printf("key=%s, value=%s\n", k, v)
}
return nil
})
或者使用 ForEach
遍历所有桶下面所有key
db.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
b := tx.Bucket([]byte("MyBucket"))
b.ForEach(func(k, v []byte) error {
fmt.Printf("key=%s, value=%s\n", k, v)
return nil
})
return nil
})
key范围遍历
db.View(func(tx *bolt.Tx) error {
c := tx.Bucket([]byte("Events")).Cursor()
min := []byte("1990-01-01T00:00:00Z")
max := []byte("2000-01-01T00:00:00Z")
for k, v := c.Seek(min); k != nil && bytes.Compare(k, max) <= 0; k, v = c.Next() {
fmt.Printf("%s: %s\n", k, v)
}
return nil
})
key前缀遍历
db.View(func(tx *bolt.Tx) error {
c := tx.Bucket([]byte("MyBucket")).Cursor()
prefix := []byte("1234")
for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() {
fmt.Printf("key=%s, value=%s\n", k, v)
}
return nil
})