Golang笔记

bolt 数据库的事务

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!
Captcha image
Relative Articles