BoltDB

1.简介

BoltDB是一个嵌入式key/value的数据库,即只需要将其链接到你的应用程序代码中即可使用BoltDB提供的API来高效的存取数据。而且BoltDB支持完全可序列化的ACID事务,让应用程序可以更简单的处理复杂操作。

BoltDB设计源于LMDB,具有以下特点:

  • 直接使用API存取数据,没有查询语句;
  • 支持完全可序列化的ACID事务,这个特性比LevelDB强;
  • 数据保存在内存映射的文件里。没有wal、线程压缩和垃圾回收;
  • 通过COW技术,可实现无锁的读写并发,但是无法实现无锁的写写并发,这就注定了读性能超高,但写性能一般,适合与读多写少的场景。

最后,BoltDB使用Golang开发,而且被应用于influxDB项目作为底层存储。

2.安装

go get github.com/boltdb/bolt

3.创建与打开数据库

	db, e := bolt.Open(dbname, 0644, nil)

open函数第一个参数为数据库名,dbname已经定义好了,这里直接引用的,第二个参数为权限,第三个参数为options

如果数据库没有则创建,如果有就打开。

4.读写事务

db.Update(func(tx *bolt.Tx) error {

		bucket := tx.Bucket([]byte(bucketname))
		//如果表为nil,则创建,不为nil则删除再创建
		if bucket == nil {
			//创建bucket
			createBucket, i := tx.CreateBucket([]byte(bucketname))
			HandleErr(i, "tx.createbucket")
		} else {
			//删除旧的bucket,新建一个空的bucket
			tx.DeleteBucket([]byte(bucketname))
			createBucket, i := tx.CreateBucket([]byte(bucketname))
			HandleErr(i, "tx.createbucket")
			

		}
    return nil
})
//只读事务
err := db.View(func(tx *bolt.Tx) error {
    ...
    return nil
})

 

  • 通过该接口可以实现数据更新操作
  • 该操作会被当做一个事务来处理,如果Update()内的操作返回nil,则事务会被提交,否则事务会回滚

 

小案例

实现区块链的持久化操作

blockchaing.go

package Block

import (
	"github.com/bolt-master/bolt-master"
	"fmt"
)

const bucketname = "blockchain"
const dbname = "bc2.db"

//定义区块链
type BlockChain struct {
	Db *bolt.DB //存放区块的数据库
}

//创造区块链
func (bc *BlockChain) CreateBlockChain() {
	//创造创世区块
	firstBlock := new(Block)
	block := CreateBlock(firstBlock, "创世区块")
	fmt.Println("\nfirstBlock is created!\n", block)


	//打开数据库
	db, e := bolt.Open(dbname, 0644, nil)
	HandleErr(e, "bolt.open err")



	bc.Db = db

	db.Update(func(tx *bolt.Tx) error {

		bucket := tx.Bucket([]byte(bucketname))
		//如果表为nil,则创建,不为nil则删除再创建
		if bucket == nil {
			//创建bucket
			createBucket, i := tx.CreateBucket([]byte(bucketname))
			HandleErr(i, "tx.createbucket")

			//将创始区块添加到数据库中
			puterr := createBucket.Put([]byte(block.HashCode), block.BlockToByte())
			HandleErr(puterr, "createBucket.put")
			puterr2 := createBucket.Put([]byte("LastBlockHash"), []byte(block.HashCode))
			HandleErr(puterr2, "createBucket.put")

		} else {
			//删除旧的bucket,新建一个空的bucket
			tx.DeleteBucket([]byte(bucketname))

			createBucket, i := tx.CreateBucket([]byte(bucketname))
			HandleErr(i, "tx.createbucket")
			//将创始区块添加到数据库中

			puterr1 := createBucket.Put([]byte(block.HashCode), block.BlockToByte())
			HandleErr(puterr1, "createBucket.put")

			puterr2 := createBucket.Put([]byte("LastBlockHash"), []byte(block.HashCode))
			HandleErr(puterr2, "createBucket.put")
		}

		return nil
	})

}

//向区块链中添加区块
func (bc *BlockChain) AddBlock(data string) {
	//连接数据库进行更改
	bc.Db.Update(func(tx *bolt.Tx) error {
		//查看表是否存在
		bucket := tx.Bucket([]byte(bucketname))
		//如果存在
		if bucket != nil {
			//获取lastblockhash
			lastblockbyte := bucket.Get([]byte("LastBlockHash"))


			//如果lastblock不存在则panic
			if lastblockbyte == nil {
				panic("bc.lastblockhash is worry !")
				//如果存在则继续
			} else {

				byteblock := bucket.Get(lastblockbyte)

				//格式转换
				block := ByteToBlock(byteblock)

				newblock := CreateBlock(block, data)

				fmt.Println(data,newblock)


				//存入数据库
				puterr1 := bucket.Put([]byte(newblock.HashCode), newblock.BlockToByte())
				HandleErr(puterr1, "createBucket.put")
				//更改数据库中的lastblockhash
				puterr2 := bucket.Put([]byte("LastBlockHash"), []byte(newblock.HashCode))
				HandleErr(puterr2, "createBucket.put")

			}
		}else{
			panic("bucket is not exist !" )

		}
		return nil
	})

}
//循环输出block
func (bc *BlockChain) ShowBlockChain() {

	bc.Db.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(bucketname))

		if bucket != nil {
			lastblockhash := bucket.Get([]byte("LastBlockHash"))

			if lastblockhash == nil {
				panic("lastblock is not exist !")
			} else {
				lastblock := bucket.Get([]byte(lastblockhash))
				toBlock := ByteToBlock(lastblock)


				fmt.Println()
				fmt.Println(toBlock)

				for {
					hash:=toBlock.PreHash
					get := bucket.Get([]byte(hash))
					toBlock := ByteToBlock(get)
					fmt.Println(toBlock)
					if toBlock.PreHash==""{
						break
					}

				}
			}

		} else {
			panic("bucket is not exist !")
		}
		return nil
	})

}

 block.go

package Block

import (
	"time"
	"encoding/gob"
	"bytes"
)

type Block struct {
	Index     int
	HashCode  string
	PreHash   string
	Timestamp string
	Data      string
	Nonce     int
	Diff      int
}

//创造区块
func CreateBlock(oldBlock *Block,data string)*Block{
	block := new(Block)
	block.Data=data
	block.PreHash=oldBlock.HashCode
	block.Timestamp=time.Now().String()
	block.Diff=4
	block.Index=oldBlock.Index+1
	Pow(block)
	return block
}
func (b *Block) BlockToByte()[]byte{
	var buffer  bytes.Buffer
	decoder := gob.NewEncoder(&buffer)
	decode := decoder.Encode(b)
	HandleErr(decode,"decoder worry !" )
	return buffer.Bytes()
}
func ByteToBlock(bb []byte) *Block{
	b := new(Block)
	decoder:= gob.NewDecoder(bytes.NewReader(bb))
	decoder.Decode(b)
	return b

}

 pow.go

package Block

import (
	"strconv"
	"crypto/sha256"
	"encoding/hex"
	"strings"
	"fmt"
)

func Pow(block *Block){
	for{
		hash := GetHash(block)
		fmt.Println(hash,"\t",block.Nonce)
		repeat := strings.Repeat("0", block.Diff)
		prefix := strings.HasPrefix(hash, repeat)
		if prefix{
			fmt.Println("挖矿成功!")
			block.HashCode=hash
			break
		}else {
			block.Nonce+=1
		}
	}
}


func GetHash(block *Block)string{

	s:=strconv.Itoa(block.Index)+strconv.Itoa(block.Nonce)+
		strconv.Itoa(block.Diff)+block.Timestamp+block.PreHash+
		block.Data
	hashed := sha256.New()
	hashed.Write([]byte(s))
	hash := hashed.Sum(nil)
	return hex.EncodeToString(hash)
}

          

uitls.go

package Block

import "fmt"

func HandleErr(err error,why string){
	if err!=nil{
		fmt.Println(err,"\twhy=",why)
	}
}


 

main.go

package main

import (
	"go_demo/BlockChain2/Block"
	"strconv"
)

func main() {

	var blockchain Block.BlockChain
	blockchain.CreateBlockChain()
	defer blockchain.Db.Close()

	blockchain.AddBlock("我是第" + strconv.Itoa(2) + "个区块")

	blockchain.ShowBlockChain()

}

结果为:

 

 BoltDB_第1张图片

你可能感兴趣的:(go语言小案例)