zoukankan      html  css  js  c++  java
  • Boltdb学习笔记之〇--概述

    更多精彩内容,请关注微信公众号:后端技术小屋

    看了boltdb也有一阵子了,看完之后总想写点什么,因为感觉到这可能是个不小的坑,所以迟迟没有动笔(没错我的拖延症又犯了..)。最近有一种流行的说法:如果一个东西不能把它讲清楚,便不能说你学会了它。因为看起来会和真的会之间有一个巨大的鸿沟,想跨越这个鸿沟便需要不断的提问、思考与输出,这是个相对枯燥但绝对值得的过程,因此趁着周末两天的完整时间正式开始挖坑。

    什么是boltdb

    Boltdb是一个go语言开发的嵌入式kv数据库。其实现相对简单:

    • 不支持网络请求和SQL查询,因此也就没有了网络交互、词法分析、语法分析、查询优化等成熟数据库中必不可少的功能。
    • 使用了比较少见的shadow page技术,只支持一个writer和多个reader,在这种约束下,事务的隔离级别为可串行话,并发控制也比较简单
    • 使用mmap将内存与磁盘建立映射,由OS管理磁盘page load到内存的过程,大大减少了boltdb手动管理的复杂度。

    Boltdb所有代码加起来才1W行,但是麻雀虽小五脏俱全,非常适合用来学习数据库中的一些基本原理和概念,例如page、transanction、cursor等。

    值得一提的是,Boltdb还是etcd底层的kv存储,目前Boltdb原仓库(https://github.com/boltdb/bolt)已经是read-only状态。而etcd维护了一个fork(https://github.com/etcd-io/bbolt), 主要是为了继续增强可靠性、稳定性和性能。

    如何使用boltdb

    数据模型

    在使用boltdb之前,我们需要对其数据模型有个直观的了解。以下是boltdb与关系型数据库的数据模型简单类比:

    boltdb中的概念 关系型数据库中的概念
    DB database
    Bucket table
    key value pair Tuple

    Boltdb中的Bucket虽然可简单类比成关系型数据中table,有一点却不相同:前者可嵌套创建Bucket, 即一个Bucket下还可创建子Bucket, 而后者不行。

    安装

    go get github.com/boltdb/bolt/...  
    

    操作DB

    操作DB包括创建(打开)、关闭。

    代码如下:在执行bolt.Open时,如果指定文件路径不存在,则根据路径创建一个数据库文件;否则加载该路径下的文件。使用db.Close便可关闭DB.

    package main  
      
    import (  
    	"log"  
      
    	"github.com/boltdb/bolt"  
    )  
      
    func main() {  
    	// Open the my.db data file in your current directory.  
    	// It will be created if it doesn't exist.  
    	db, err := bolt.Open("my.db", 0600, nil)  
    	if err != nil {  
    		log.Fatal(err)  
    	}  
    	defer db.Close()  
      
    	...  
    }  
    

    操作事务

    Boltdb中按照是否只读将事务分为读事务和写事务。

    用户使用db.View创建读事务时需传入一个回调函数,表示读事务执行操作。如果回调函数返回的err != nildb.View则会回滚该事务,并将err透传给db.View

    err := db.View(func(tx *bolt.Tx) error {  
    	...  
    	return nil  
    })  
    

    使用db.Update可创建写事务。db.Update如何处理错误同db.View

    err := db.Batch(func(tx *bolt.Tx) error {  
    	...  
    	return nil  
    })  
    

    操作Bucket

    操作Bucket包括创建Bucket、删除Bucket

    创建Bucket属于写事务。这里db.Update会创建一个写事务,写事务执行的操作是CreateBucket,即创建一个新的Bucket

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

    删除Bucket也属于写事务。使用上同理

    db.Update(func(tx *bolt.Tx) error {  
    	b, err := tx.DeleteBucket([]byte("MyBucket"))  
    	if err != nil {  
    		return fmt.Errorf("create bucket: %s", err)  
    	}  
    	return nil  
    })  
    

    操作key/value

    操作key/value包括:新建/更新/删除/查询。所有的key/value对都必须属于某个具体的Bucket. 因此操作key/value之前必须找到Bucket对象。

    新建/更新代码必须用写事务封装,代码如下,这里在名为MyBucket的Bucket下新增了一对("answer", "42")

    db.Update(func(tx *bolt.Tx) error {  
    	b := tx.Bucket([]byte("MyBucket"))  
    	err := b.Put([]byte("answer"), []byte("42"))  
    	return err  
    })  
    

    删除代码如下:

    db.Update(func(tx *bolt.Tx) error {  
    	b := tx.Bucket([]byte("MyBucket"))  
    	err := b.Delete([]byte("answer")  
    	return err  
    })  
    

    查询代码如下:

    db.View(func(tx *bolt.Tx) error {  
    	b := tx.Bucket([]byte("MyBucket"))  
    	v := b.Get([]byte("answer"))  
    	fmt.Printf("The answer is: %s
    ", v)  
    	return nil  
    })  
    

    如何分析Boltdb

    代码导读

    首先是读代码,从微观到宏观的层面了解这座房屋如何建成的。代码阅读顺序是

    page.go: 磁盘上的page layout,包括meta page, freelist page, branch page, leaf page。  
    node.go: 磁盘上的page反序列化到内存之后的数据结构,也作为B+树节点。  
    freelist.go: page管理, 支持page申请、释放、回滚等操作。  
    cursor.go: 用于访问B+树的迭代器  
    bucket.go: Bucket数据结构,支持创建/删除子Bucket、新建/更新/删除kv数据。  
    db.go.go: 用于访问DB, 支持打开/关闭DB、创建读/写事务、db file自动扩容。  
    

    更详细的代码细节将在该系列的后续内容中给出.

    分析工具

    Boltdb提供了一个好用的工具,可用于查看db file中每个page的内容

    安装:

    git clone https://github.com/boltdb/bolt   
    cd cmd/bolt  
    go build  
    ls ./bolt  
    

    查看所有pages状态

    $ ./bolt  pages  /tmp/bolt.db   | head  
    ID       TYPE       ITEMS  OVRFLW  
    ======== ========== ====== ======  
    0        meta       0              
    1        meta       0              
    2        freelist   4              
    3        leaf       141            
    4        leaf       86             
    5        leaf       85             
    6        branch     117            
    7        leaf       85      
    

    其中ID表示page id, TYPE为page类型,ITEMS表示其中的数据条数,OVRFLW表示该page是否溢出。

    查看某个page的内容

    $ ./bolt  page /tmp/bolt-624750664  3  | head  
    Page ID:    3  
    Page Type:  leaf  
    Total Size: 4096 bytes  
    Item Count: 141  
      
    "9874": "9874"  
    "9875": "9875"  
    "9876": "9876"  
    "9877": "9877"  
    "9878": "9878"  
    

    以上为某个leaf page的内容,底部为该page中存储的key/value对。

    推荐阅读

    更多精彩内容,请扫码关注微信公众号:后端技术小屋。如果觉得文章对你有帮助的话,请多多分享、转发、在看。
    二维码

  • 相关阅读:
    【排序】SelectSort
    Linux下程序的Profile工具
    Ubuntu adb devices :???????????? no permissions 解决方法
    利用宏控制打印
    关于错误 Resource temporarily unavailable
    如何不使用pthread_cancel而杀死线程
    【排序】BubbleSort
    使用 autotools 生成包含多文件的 Makefile
    source命令使用
    2010 成都预选赛 Binary Number
  • 原文地址:https://www.cnblogs.com/taiyang-li/p/14496957.html
Copyright © 2011-2022 走看看