bolt 数据库的事务
🕧 Tue, 30 Aug 2016 by GolangNote
Bolt类似于LMDB,这个被认为是在现代kye/value存储中最好的。但是又不同于LevelDB,BoltDB支持完全可序列化的ACID事务
打开数据库文件:
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()
函数来进行读-写
事务:
使用 err := db.Update(func(tx *bolt.Tx) error {
...
return nil
})
在函数内部你可以get/set数据和处理error。如果返回为nil,事务就会从数据库得到一个commit,但是如果你返回一个实际的错误,则会做回滚,你在函数中做的任何事情都不会commit到磁盘上。
db.View
函数来做只读
事务:
使用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数据库:
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"))
})
读取数据:
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
:
使用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:
// 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
...
}
批量读写事务 :
err := db.Batch(func(tx *bolt.Tx) error {
...
return nil
})
遍历键:
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
})
和游标相关的函数:
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.
键前缀查找:
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
})
键范围扫描:
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
:
使用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
:
嵌套func (*Bucket) CreateBucket(key []byte) (*Bucket, error)
func (*Bucket) CreateBucketIfNotExists(key []byte) (*Bucket, error)
func (*Bucket) DeleteBucket(key []byte) error
Tx.WriteTo()
备份bolt 数据:
使用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)
}
}
可以实现在线热备份
$ curl http://localhost/backup > my.db
也可以用 Tx.CopyFile()
函数来copy 一个备份文件。
用只读模式打开数据库:
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 (转载注明出处)
关于GolangNote:记录在工作中使用golang 遇到、面临的相关问题及解决方法。如果你在这里获得一些知识或信息,解决你的编程问题,请考虑捐赠给不幸的人或者你喜欢的慈善机构,除捐赠外,种植树木、志愿服务或减少排碳的行为也很有益处。如果你有任何问题可以在下面 留言
Be the first to comment!
Relative Articles
Recent Go Articles
- Golang 把cookie 字符串解析为cookie 结构
- Golang 计算字符串中包含某个或某些字符集的个数
- 使用Golang 对文件增删写读操作备忘
- Go Modules 使用备忘
- 使用Golang 简单删除图片exif 信息
- 谷歌翻译的 golang 库推荐
- Go 1.13.2 与1.13.3 紧急更新
- golang 人脸检测识别库
- Go build 错误 “stackcheck redeclared in this block previous declaration”的解决方法
- Golang phantomjs 动态代理实现
- Golang chrome debug protocol 库推荐
- Golang 随机打乱数组/Slice
- Golang sync.WaitGroup 的 Wait 超时处理
- Golang实现简单的Socks5代理
- Golang 用snappy + Base64 简单压缩加密进行网络传输
- Golang http IPv4/IPv6 服务
- golang 全角半角相互转换
- 在自己的网站部署TLS 1.3
- Golang 实现/打印菜单树
- Golang telegram 机器人小试
Top Go Articles
- Golang实现简单的Socks5代理
- goLang 实现排列组合的代码
- 用Go语言写一个最简单的echo服务器
- golang 用正则包regexp 通过user-agent 识别手机浏览器
- Go build 错误 “stackcheck redeclared in this block previous declaration”的解决方法
- Golang 字符串毫秒转时间格式
- Golang 简单的任务队列
- Golang telegram 机器人小试
- Golang 定时循环的实现
- golang 用 crypto/bcrypt 存储密码的例子
- Golang 生成防识别的图片验证码
- golang 实现Authenticator 二次验证,可用到web 登录
- golang 为Windows XP/Server 2003 编译程序
- Golang io.ReadCloser 和[]byte 相互转化的方法
- groupcache 使用入门
- golang 用gzip 压缩、解压缩字符串
- chroma: 纯go 实现的类似Pygments 的代码高亮库
- golang flate/zlib 解压缩
- golang 生成良好的唯一ID/uuid库比较
- Golang 生成 session id