Bolt 类似于 LMDB,这个被认为是在现代 kye/value 存储中最好的。但是又不同于 LevelDB,BoltDB 支持完全可序列化的ACID事务
打开数据库文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
import (
"log"
"github.com/boltdb/bolt"
)
func main() {
db, err := bolt.Open("golangnote.db", 0600, nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// ...
}
使用 DB.Update()
函数来进行读-写
事务:
1
2
3
4
err := db.Update(func(tx *bolt.Tx) error {
...
return nil
})
在函数内部你可以 get/set
数据和处理 error 。如果返回为 nil
,事务就会从数据库得到一个 commit
,但是如果你返回一个实际的错误,则会做回滚,你在函数中做的任何事情都不会 commit
到磁盘上。
使用db.View
函数来做只读
事务:
1
2
3
4
err := db.View(func(tx *bolt.Tx) error {
// ... read only
return nil
})
Bolt 是一个 k/v 的存储并提供了一个映射表,这意味着你可以通过 name 拿到值,就像 Go 原生的 map,但是另外因为 key
是有序的,所以你可以通过 key
来遍历。
这里还有另外一层:k-v 存储在 bucket
中,你可以将 bucket
当做一个 key
的集合或者是数据库中的表。(buckets中可以包含其他的buckets)
你可以通过下面方法 update 数据库:
1
2
3
4
5
6
7
db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte("posts"))
if err != nil {
return err
}
return b.Put([]byte("2015-01-01"), []byte("My New Year post"))
})
读取数据:
1
2
3
4
5
6
db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("posts"))
v := b.Get([]byte("2015-01-01"))
fmt.Printf("%sn", v)
return nil
})
使用buckets
:
1
2
3
4
5
6
7
db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("MyBucket"))
if err != nil {
return fmt.Errorf("create bucket: %s", err)
}
return nil
})
简单获取自增id:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// CreateUser saves u to the store. The new user ID is set on u once the data is persisted.
func (s *Store) CreateUser(u *User) error {
return s.db.Update(func(tx *bolt.Tx) error {
// Retrieve the users bucket.
// This should be created when the DB is first opened.
b := tx.Bucket([]byte("users"))
// Generate ID for the user.
// This returns an error only if the Tx is closed or not writeable.
// That can't happen in an Update() call so I ignore the error check.
id, _ := b.NextSequence()
u.ID = int(id)
// Marshal user data into bytes.
buf, err := json.Marshal(u)
if err != nil {
return err
}
// Persist bytes to users bucket.
return b.Put(itob(u.ID), buf)
})
}
// itob returns an 8-byte big endian representation of v.
func itob(v int) []byte {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, uint64(v))
return b
}
type User struct {
ID int
...
}
批量读写事务 :
1
2
3
4
err := db.Batch(func(tx *bolt.Tx) error {
...
return nil
})
遍历键:
1
2
3
4
5
6
7
8
9
10
11
12
db.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
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
})
和游标相关的函数:
1
2
3
4
5
First() Move to the first key.
Last() Move to the last key.
Seek() Move to a specific key.
Next() Move to the next key.
Prev() Move to the previous key.
键前缀查找:
1
2
3
4
5
6
7
8
9
10
11
db.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
c := tx.Bucket([]byte("MyBucket")).Cursor()
prefix := []byte("1234")
for k, v := c.Seek(prefix); bytes.HasPrefix(k, prefix); k, v = c.Next() {
fmt.Printf("key=%s, value=%s\n", k, v)
}
return nil
})
键范围扫描:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
db.View(func(tx *bolt.Tx) error {
// Assume our events bucket exists and has RFC3339 encoded time keys.
c := tx.Bucket([]byte("Events")).Cursor()
// Our time range spans the 90's decade.
min := []byte("1990-01-01T00:00:00Z")
max := []byte("2000-01-01T00:00:00Z")
// Iterate over the 90's.
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
})
使用ForEach()
函数来遍历bucket
:
1
2
3
4
5
6
7
8
9
10
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
})
嵌套buckets
:
1
2
3
func (*Bucket) CreateBucket(key []byte) (*Bucket, error)
func (*Bucket) CreateBucketIfNotExists(key []byte) (*Bucket, error)
func (*Bucket) DeleteBucket(key []byte) error
使用Tx.WriteTo()
备份bolt 数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
func BackupHandleFunc(w http.ResponseWriter, req *http.Request) {
err := db.View(func(tx *bolt.Tx) error {
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", `attachment; filename="my.db"`)
w.Header().Set("Content-Length", strconv.Itoa(int(tx.Size())))
_, err := tx.WriteTo(w)
return err
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
可以实现在线热备份
1
$ curl http://localhost/backup > my.db
也可以用 Tx.CopyFile()
函数来 copy 一个备份文件。
用只读模式打开数据库:
1
2
3
4
5
db, err := bolt.Open("my.db", 0666, &bolt.Options{ReadOnly: true})
// db, err := bolt.Open("my.db", 0600, &bolt.Options{Timeout: 1 * time.Second}) // 指定超时时间
if err != nil {
log.Fatal(err)
}
使用 Bolt 的命令行
BoltDB 提供了一个名叫 bolt 的命令行工具,你可以列出 buckets 和 keys、检索values、一致性检验。
用法可以使用--help查看。
例如,检查 blog.db
数据库的一致性,核对每个页面: bolt check blog.db
本文网址: https://golangnote.com/topic/128.html 转摘请注明来源