Golang 数据库 boltDB
文章目录
一、boltDB 介绍
1. boltDB 简介
- BoltDB 是一个纯粹的 Key/Value 模型的程序。该项目的目标是为不需要完整数据库服务器(如 MySQL)的项目提供一个简单,快速,可靠的数据库
- 只需要将 BoltDB 链接到应用程序代码中即可使用它提供的 API 来存取数据。BoltDB 支持完全可序列化的 ACID 事务,可以处理复杂操作。
- Bolt 是由 B+Tree 实现的
2. boltDB 特点
- BoltDB 的设计源于 LMDB,它具有以下特点:
- 直接使用 API 存取数据,没有查询语句
- 支持完全可序列化的 ACID 事务
- 数据保存在内存映射的文件里
- 通过 COW 技术,实现无锁的读写并发,但是无法实现无锁的写写并发,这就注定了读性能超高,但写性能一般,适合读多写少的场景
- BoltDB 使用 Golang 开发,被应用于 influxDB 项目作为底层存储
总的来说,boltDB 适用于内存 > 数据库,读 > 写,稳定 > 性能 的场合
二、boltDB 安装
1. 安装 BoltDB
Linux 环境下
go get github.com/boltdb/bolt
Windows 环境
- 需要事先安装好 Go 环境
- 安装git http://git-for-windows.github.io/
- 执行命令 go get github.com/boltdb/bolt/…
- 安装第三方包。这条命令它会把类库包源代码,下载解压到你的 %GOPATH% 路径里面去,并且它还会同时执行 go install xxx ,生成 包路径
- 在代码中导入第三方包
import (
"github.com/boltdb/bolt"
)
使用 BoltDB 的样例
package main
import (
"log"
"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 {
...
return nil
})
- 只读事务,可以进行数据查询操作
err := db.View(func(tx *bolt.Tx) error {
...
return nil
})
三、boltDB 使用
以创建一个包含 Article 和 User 的博客网站数据库为例
- Blog.db 有两个 Bucket:
- value:article;key:articleID
- value:user;key:userName
1. 数据库操作封装
将对数据库的访问操作,封装成函数存放在 db.go 文件
- db.go 中,Init 用于创建和启动数据库。
- open 的第一个参数为路径,如果数据库不存在则会创建名为 my.db 的数据库,第二个为文件操作,第三个参数是可选参数,内部可以配置 只读和超时时间等
//get the database absolute path
func DBPATH() string {
pt, _ := os.Getwd()
fmt.Println(path.Join(pt ,"/source/Blog.db"))
return path.Join(pt ,"/source/Blog.db")
}
//create the bucket for article and user
func Init() {
db, err := bolt.Open(DBPATH(), 0600, nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("article"))
if b == nil {
_, err := tx.CreateBucket([]byte("article"))
if err != nil {
log.Fatal(err)
}
}
b = tx.Bucket([]byte("user"))
if b == nil {
_, err := tx.CreateBucket([]byte("user"))
if err != nil {
log.Fatal(err)
}
}
return nil
})
if err != nil {
log.Fatal(err)
}
}
- boltdb 是文件操作类型的数据库,所以只能单点写入和读取,如果多个同时操作的话后者会被挂起直到前者关闭操作为止,数据具有较强的一致性。
2. 数据库的 CURD 操作
- 对数据库的 CURD 实现,即创建(Create)、更新(Update)、读取(Retrieve)和删除(Delete)操作,具体步骤如下
打开数据库
获取一个事务 tx
根据 tx 获取bucket b
进行更新——b.Put(key, data)
进行查询——b.Get(key)
下面以 Articles 和 Users 为例
① Put
- 存取键值对到桶里
// PutArticle : put one article to blog.db
func PutArticle(article Article) error {
db, err := bolt.Open(DBPATH(), 0600, nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("article"))
if b != nil {
key := make([]byte, 8)
binary.LittleEndian.PutUint64(key, uint64(article.Id))
data, _ := json.Marshal(article)
b.Put(key, data)
}
return nil
})
if err != nil {
return err
}
return nil
}
func PutUser(user User) error {
db, err := bolt.Open(DBPATH(), 0600, nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("user"))
if b != nil {
data, _ := json.Marshal(user)
b.Put([]byte(user.Username), data)
}
return nil
})
if err != nil {
return err
}
return nil
}
② Get
- 从桶里获取键值对
// Get Atricle with ID,ID=-1 means get all articles
func GetArticles(id int64, page int64) []Article {
db, err := bolt.Open(DBPATH(), 0600, nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
articles := make([]Article, 0)
err = db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("article"))
if b != nil && id >= 0 {
key := make([]byte, 8)
binary.LittleEndian.PutUint64(key, uint64(id))
data := b.Get(key)
if data != nil {
atc := Article{}
err := json.Unmarshal(data, &atc)
if err != nil {
log.Fatal(err)
}
articles = append(articles, atc)
}
} else if b != nil && id == -1 {
cursor := b.Cursor()
nPerPage := 5
fromKey := make([]byte, 8)
binary.LittleEndian.PutUint64(fromKey, uint64(page-1)*(uint64)(nPerPage+1))
for k, v := cursor.Seek(fromKey); k != nil && nPerPage > 0; k, v = cursor.Next() {
atc := Article{}
err := json.Unmarshal(v, &atc)
if err != nil {
log.Fatal(err)
}
articles = append(articles, atc)
nPerPage--
}
}
return nil
})
if err != nil {
log.Fatal(err)
}
return articles
}
//Get user information with the username
func GetUser(username string) User {
db, err := bolt.Open(DBPATH(), 0600, nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
user := User{}
err = db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("user"))
if b != nil {
data := b.Get([]byte(username))
if data != nil {
err := json.Unmarshal(data, &user)
if err != nil {
log.Fatal(err)
}
}
}
return nil
})
if err != nil {
log.Fatal(err)
}
return user
}
3. 数据库应用实例
- 将 Article 和 User 数据写入到数据库中
func writeOneBlog(id int64,title string,author string,tags []db.Tag,date string,content string,comments []db.Comment){
articles := db.Article{id,title,author,tags,date,content,comments}
users := db.User{author,author}
db.PutArticle(articles)
db.PutUser(users)
}