BoltDB数据库学习

BlotDB数据库是k/v非关系型数据库。

使用github.com/blotdb/bolt包实现Go语言的BoltDB

在Go语言中具体的使用方式如下:

创建&打开数据库

1
2
3
4
5
6
7
8
//Bolt会在数据文件上获得一个文件锁,所以多个进程不能同时打开同一个数据库
//打开一个已经打开的Bolt数据库将导致它挂起,直到另一个进程关闭它
//为防止无限期等待,可以将超时选项传递给Open()函数
db,err := bolt.Open("my.db" , 0600 , &bolt.Options{Timeout: 10*time.Second})
defer db.Close()
if err != nil {
log.Panic(err)
}

写数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//两种处理方式:读-写和只读操作,读写方式开始于db.Update()方法
//Bolt一次只允许一个读-写事务,但是一次允许多个只读事务
//每个事务处理都有一个始终如一的数据视图
err = db.Update(func(tx *bolt.Tx) error {
//这里还有另外一层:k-v存储在bucket种
//可以将bucket当作一个key的集合或者是数据库中的表
//buckets中可以包含其他的buckets
//Buckets使键值对在数据库中的集合。所有在bucket中的key必须唯一
//使用DB.CreateBucket()函数建立bucket
//Tx.DeleteBucket()删除bucket
//b:=tx.Bucket([]byte("MyBucket"))
b , err := tx.CreateBucketIfNotExists([]byte("MyBucket"))
//这要将key-value对保存到bucket,请使用Bucket.Put()函数
//这将在MyBucket的bucket中将“answer”key的值设置为“42”
err = b.Put([]byte("answer") , []byte("42"))
err = b.Put([]byte("why") , []byte("101010"))
return err

})

读数据

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
36
37
38
39
40
41
42
//只读事务在db.View函数中可以读取,但是不能修改。
db.View(func(tx *bolt.Tx) error {
//要检索这个value,可以使用Bucket.Get()函数
//由于Get是有安全保障的,所以不会返回错误,不存在的key会返回nil
b := tx.Bucket([]byte("MyBucket"))
v := b.Get([]byte("answer"))
id , _ := b.NextSequence()
fmt.Printf("The answer is : %s,%d\n" , v , id)

//游标遍历
c := b.Cursor()
fmt.Printf("\n游标遍历key")
for k , v := c.First() ; k!=nil;k,v=c.Next(){
fmt.Printf("key = %s , value = %s\n" , k ,v)
}

//游标上有以下函数
//First(),移动到第一个键
//Last(),移动到最后一个键
//Seek(),移动到特定的一个键
//Next(),移动到下一个键
//Prev(),移动到上一个键

//Prefix 前缀扫描
fmt.Printf("\nPrefix 前缀扫描")
prefix := []byte("a")
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
})

//如果直到所在桶中拥有键,也可以使用ForEach()来迭代
db.View(func(tx *bolt.Tx) error {
fmt.Println("\nForEach()来迭代")
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
})

事务处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//事务处理
//开始事务
tx,err := db.Begin(true)
if err!=nil{
log.Panic(err)
}
defer tx.Rollback()

//使用事务
_,err = tx.CreateBucket([]byte("MyBucket"))
if err!=nil{
log.Panic(err)
}

//事务提交
if err = tx.Commit() ; err != nil{
log.Panic(err)
}

使用案例

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package main

import (
"bytes"
"fmt"
"github.com/boltdb/bolt"
"log"
"time"
)

func main() {
BoltDB()
}

func BoltDB(){
//Bolt会在数据文件上获得一个文件锁,所以多个进程不能同时打开同一个数据库
//打开一个已经打开的Bolt数据库将导致它挂起,直到另一个进程关闭它
//为防止无限期等待,可以将超时选项传递给Open()函数
db,err := bolt.Open("my.db" , 0600 , &bolt.Options{Timeout: 10*time.Second})
defer db.Close()
if err != nil {
log.Panic(err)
}

//两种处理方式:读-写和只读操作,读写方式开始于db.Update()方法
//Bolt一次只允许一个读-写事务,但是一次允许多个只读事务
//每个事务处理都有一个始终如一的数据视图
err = db.Update(func(tx *bolt.Tx) error {
//这里还有另外一层:k-v存储在bucket种
//可以将bucket当作一个key的集合或者是数据库中的表
//buckets中可以包含其他的buckets
//Buckets使键值对在数据库中的集合。所有在bucket中的key必须唯一
//使用DB.CreateBucket()函数建立bucket
//Tx.DeleteBucket()删除bucket
//b:=tx.Bucket([]byte("MyBucket"))
b , err := tx.CreateBucketIfNotExists([]byte("MyBucket"))
//这要将key-value对保存到bucket,请使用Bucket.Put()函数
//这将在MyBucket的bucket中将“answer”key的值设置为“42”
err = b.Put([]byte("answer") , []byte("42"))
err = b.Put([]byte("why") , []byte("101010"))
return err

})
//可以看到,传入db.update函数一个参数,在函数内部可以get/set数据和处理error
//如果返回为nil,事务就会从数据库得到一个commit,但如果返回一个实际的错误,就会回滚,在函数中做的任何事情都不会commit
//只读事务,只读事务和读写事务不应该相互依赖,一般不应该在同一个例程中同时打开。
//这可能会导致死锁,因为读写事务需要定期重新映射数据文件
//但只有在只读事务处于打开状态时才能这样做

//批量读写事务。每一次新的事务都需要等待上一次事务的结束。
//可以通过DB.Batch()批处理来完成
err = db.Batch(func(tx *bolt.Tx) error {
return nil
})

//只读事务在db.View函数中可以读取,但是不能修改。
db.View(func(tx *bolt.Tx) error {
//要检索这个value,可以使用Bucket.Get()函数
//由于Get是有安全保障的,所以不会返回错误,不存在的key会返回nil
b := tx.Bucket([]byte("MyBucket"))
v := b.Get([]byte("answer"))
id , _ := b.NextSequence()
fmt.Printf("The answer is : %s,%d\n" , v , id)

//游标遍历
c := b.Cursor()
fmt.Printf("\n游标遍历key")
for k , v := c.First() ; k!=nil;k,v=c.Next(){
fmt.Printf("key = %s , value = %s\n" , k ,v)
}

//游标上有以下函数
//First(),移动到第一个键
//Last(),移动到最后一个键
//Seek(),移动到特定的一个键
//Next(),移动到下一个键
//Prev(),移动到上一个键

//Prefix 前缀扫描
fmt.Printf("\nPrefix 前缀扫描")
prefix := []byte("a")
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
})

//如果直到所在桶中拥有键,也可以使用ForEach()来迭代
db.View(func(tx *bolt.Tx) error {
fmt.Println("\nForEach()来迭代")
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
})

//事务处理
//开始事务
tx,err := db.Begin(true)
if err!=nil{
log.Panic(err)
}
defer tx.Rollback()

//使用事务
_,err = tx.CreateBucket([]byte("MyBucket"))
if err!=nil{
log.Panic(err)
}

//事务提交
if err = tx.Commit() ; err != nil{
log.Panic(err)
}

//还可以在一个键中存储一个bucket,以创建嵌套的桶
//func(*Bucket) CreateBucket(key []byte)(*Bucket,err)
//func(*Bucket) CreateBucketNotExists(key []byte)(*Bucket,err)
//func(*Bucket) DeleteBucket(key []byte)error
}