Golang 数据库 boltDB


文章目录

  • Golang 数据库 boltDB
  • 一、boltDB 介绍
  • 1. boltDB 简介
  • 2. boltDB 特点
  • 二、boltDB 安装
  • 1. 安装 BoltDB
  • 2. 基本操作
  • 三、boltDB 使用
  • 1. 数据库操作封装
  • 2. 数据库的 CURD 操作
  • ① Put
  • ② Get
  • 3. 数据库应用实例
  • 四、参考文章


一、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)
}



四、参考文章

  • 聊聊BoltDB:简单使用
  • BoltDB简单使用教程