1. boltDB 简介
BoltDB 是一个 使用 Golang 开发的 Key/Value 模型的程序,为不需要完整数据库服务器(如 MySQL)的项目提供一个简单,快速,可靠的数据库。只需要将 BoltDB 链接到应用程序代码中即可使用它提供的 API 来存取数据,无需数据库管理语言。BoltDB 支持完全可序列化的 ACID 事务,可以处理复杂操作。通过COW技术,可实现无锁的读写并发,但是无法实现无锁的写写并发,这就注定了读性能超高,但写性能一般,适合与读多写少的场景。
boltDB 安装
需要事先安装好 Go 环境
go get github.com/boltdb/bolt
这条命令会把类库包源代码,下载解压到你的 %GOPATH% 路径里面去,并且它还会同时执行 go install xxx ,生成 包路径
没有设置go mod会默认下载到%GOPATH%的src下,设置了会下载到%GOPATH%里pkg的mod下
在代码中导入第三方包
import ("github.com/boltdb/bolt")
打开数据库示例
func main() {
db, err := bolt.Open("my.db", 0600, nil) //打开数据库文件 如果没有则创建
if err != nil { log.Fatal(err) }
defer db.Close() ... //关闭数据库
}
2. 基本操作
更新事务,该操作会被当做一个事务来处理,如果 Update() 内的操作返回nil,则事务会原子性被提交,返回错误则事务会回滚
err := db.Update(func(tx *bolt.Tx) error { //读写事件 当写操作是必须是update
...
return nil
})
只读事务,可以进行数据查询操作
err := db.View(func(tx *bolt.Tx) error { //读事件
...
return nil
})
启动事务
DB.Begin()
启动函数包含在db.update和db.batch中,该函数开始执行事务并返回结果关闭事务,这是boltdb推荐的方式,有时候你可能需要手动启动事物你可以使用Tx.Begin()
来开始,切记不要忘记关闭事务。
// Start a writable transaction.
tx, err := db.Begin(true)
if err != nil {
return err
}
defer tx.Rollback()
// Use the transaction...
_, err := tx.CreateBucket([]byte("MyBucket"))
if err != nil {
return err
}
// Commit the transaction and check for error.
if err := tx.Commit(); err != nil {
return err
}
创建桶
CreateBucket() 或者 CreateBucketIfNotExists() 建议使用后者如果桶存在则不进行操作
err = db.Update(func(tx *bolt.Tx) error { //读写事务
_, err := tx.CreateBucketIfNotExists([]byte(bucketname)) //根据视图名字创建 如果不存在则创建
if err != nil {
return fmt.Errorf("create bucket: %v", err)
}
fmt.Printf("%s已经存在",bucketname)
return nil
})
if err != nil {
log.Fatal(err)
}
删除桶
DeleteBucket() 删除桶时里面的键值对数据也会被删除
err = db.Update(func(tx *bolt.Tx) error { //读写事务
b := tx.Bucket([]byte(bucketname)) //打开视图
if b == nil {
panic("你打开的视图不存在 请重新输入")
}
er:=tx.DeleteBucket([]byte(bucketname))
if er != nil { //异常处理
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
}
}()
panic("删除失败")
}
return nil
})
if err != nil {
log.Fatal(err)
}
Put 设置
存取键值对到桶里 以[]byte存取需转换 如果这个桶下的key已经存在则value会被替换
err = db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucket)) //打开视图
if b == nil {
//异常处理
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
}
}()
panic("你打开的视图不存在 请重新输入")
}
// put 数据库插入
if err = b.Put([]byte(key), []byte(value)); err != nil {
return err
}
return err
})
Get 查询
从桶里获取键值对
db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucket)) //打开视图
if b == nil {
//异常处理
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
}
}()
panic("你打开的视图不存在 请重新输入")
}
v := b.Get([]byte(key)) // Get查询 key为answer的value
res=string(v) //转换为string
//_ := b.Cursor()
//fmt.Printf(" value=%s\n", v)
return nil
})
Delete 删除
从桶里删除键值对
err = db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucket)) //打开视图
if b == nil {
//异常处理
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
}
}()
panic("你打开的视图不存在 请重新输入")
}
// Delete 删除
if err = b.Delete([]byte(key)) ; err != nil {
fmt.Println("你要删除的key不存在")
return err
}
遍历Bucket
boltdb以桶中的字节排序顺序存储键。这使得在这些键上的顺序迭代非常快。要遍历键,我们将使用游标Cursor():
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
})
游标Cursor()允许您移动到键列表中的特定点,并一次一个地通过操作键前进或后退。
光标上有以下函数:
First() 移动到第一个键.
Last() 移动到最后一个键.
Seek() 移动到特定的一个键.
Next() 移动到下一个键.
Prev() 移动到上一个键.
可以自己组合游标封装需要的函数
利用游标实现前缀扫描
函数经过封装 可以前缀查找这个桶下以key开头的所有key-value 存储到map中
func KeyHeadGetValue(bucket string,keyhead string,mymap map[string]string){
dbptr.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
c := tx.Bucket([]byte(bucket)).Cursor()
prefix := []byte(keyhead) //把byte序列化
for k, v := c.Seek(prefix); bytes.HasPrefix(k, prefix); k, v = c.Next() {
fmt.Printf("key=%s, value=%s\n", k, v)
mymap[string(k)]=string(v)
}
return nil
})
}
桶的自增
利用nextsequence()功能,你可以让boltdb生成序列作为你键值对的唯一标识。
func (s *Store) CreateUser(u *User) error {
return s.db.Update(func(tx *bolt.Tx) error {
// 创建users桶
b := tx.Bucket([]byte("users"))
// 生成自增序列
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
...
}
数据库备份
boltdb是一个单一的文件,所以很容易备份。你可以使用TX.writeto()函数写一致的数据库。如果从只读事务调用这个函数,它将执行热备份,而不会阻塞其他数据库的读写操作。
默认情况下,它将使用一个常规文件句柄,该句柄将利用操作系统的页面缓存。有关优化大于RAM数据集的信息,请参见 Tx文档。一个常见的用例是在HTTP上进行备份,这样您就可以使用像 cURL这样的工具来进行数据库备份:
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="data.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)
}
}
然后可以使用此命令进行备份:
或者你可以打开你的浏览器以
http://localhost/backup,它会自动下载。
如果你想备份到另一个文件,你可以使用
TX.copyfile()辅助功能。
TX.copyfile()实现备份和恢复
func Dbbackup(){
db,err:=bolt.Open("./data.db", 0666, nil)
if err!=nil{
log.Fatal(err)
}
defer db.Close() //关闭数据库连接
err=db.View(func(tx *bolt.Tx) error {
//备份db文件,第一个参数path表示备份文件路径,第二个参数mode表示备份文件的操作方式,值参考Linux下文件的-rwxrwxrwx权限
err := tx.CopyFile("kstor.db",0666)
return err
})
if err!=nil{
panic("备份失败 ")
}
}
查看工具
下载查看工具
go get github.com/boltdb/boltd