bolt of a pure Go KV database

Posted by maltech on Fri, 25 Feb 2022 02:46:20 +0100

Bolt is a key value database implemented in pure go language, which supports complete ACID practical operation. Although it does not have a perfect query language like SQLite, the interface is simple and easy to use. Bolt itself manages data by using a memory mapped disk file. The logic is clear and the interface is simple and easy to use. The interface provided by bolt is the simple code below.

  db, err := bolt.Open("example.db", 0600, nil)
  if err != nil {
    log.Fatal(err)
  }
  defer db.Close()

  // When the passed function returns error, bolt will execute automatically
  // rollback operation, no manual operation is required
  err := db.Update(func(tx *bolt.Tx) error{
    // ...
  })

  // Read only substantive operations are defined in dB In the view function, you can only read
  // And iterative data
  err := db.View(func(tx *bolt.Tx)error{
    // ...
  })

bolt is inspired by LMDB (a lightweight memory mapped database). LMDB itself can provide high-performance read operations and thread safe write operations, and support ACID practical operations. bolt only planned to make a Go version of LMDB in the early days, but now it has attracted more and more attention, and the number of star s in github has exceeded 7000.

Some common relational databases, such as MySQL and Postgres, provide support for structured data storage and SQL statements. They can flexibly query and store data to meet business needs, but parsing and processing SQL layer will also bring great sales. All data access of bolt is completed through keys. The design based on B + tree storage structure makes it more efficient to read and write data. At the same time, the conventional database is often deployed separately from the program, and the transmission is completed through network serialization, which will also increase some processing delay and reduce processing efficiency. Bolt stores data through a file. Although the access is not flexible enough, it is efficient.

LevelDB also belongs to a key value database, which is implemented by Google's open source C + + language. It provides a sort data storage structure, but does not support practical operations. It stores key value data through LSM tree, and supports efficient random writing, but it is not efficient for reading, especially when the capacity of data is getting larger and larger. If you have high requirements for reading efficiency, you can consider using bolt for testing·

Code example

Bolt's key value data pairs are stored in an ordered map structure. Because they are ordered, stable iterative data can be obtained. In addition, bolt defines the bucket structure, which is similar to a collection of keys or a table similar to a database. In addition, other buckets can be stored in the bucket. The following is a simple storage and query process

err = db.Update(func(tx *bolt.Tx) error {
  b, err := tx.CreateBucketIfNotExists([]byte("posts"))
  if err != nil {
    return err
  }
  return b.Put([]byte("2017-11-20"), []byte("new post"))
})

err = db.View(func(tx *bolt.Tx) error {
  b := tx.Bucket([]byte("posts"))
  v := b.Get([]byte("2017-11-20"))
  fmt.Printf("%s", v)
  return nil
})

If you need to store a complex data structure, such as custom structure data, you need to call some serialized functions to obtain the encoded data storage, such as using JSON Marshall to code:

type User struct {
  Name string `json:"name"`
  Address string `json:"address"`
  Age int `json:age`
}
...

  err = db.Update(func(tx *bolt.Tx) error {
    b, err := tx.CreateBucketIfNotExists([]byte("users"))
    if err != nil {
      return err
    }
    encoded, err := json.Marshal(user)
    if err != nil {
      return err
    }
    return b.Put([]byte(user.Name), encoded)
  })

  err = db.View(func(tx *bolt.Tx) error {
    b := tx.Bucket([]byte("users"))
    v := b.Get([]byte("mike"))
    user := new(User)
    err := json.Unmarshal(v, user)
    if err != nil {
      return err
    }
    fmt.Printf("%+v", user)
    return nil
 })

In addition, bolt provides command-line tools to check and test the database status. Usage:

bolt command [arguments]

The commands are:

bench       run synthetic benchmark against bolt
check       verifies integrity of bolt database
compact     copies a bolt database, compacting it in the process
info        print basic info
help        print this screen
pages       print list of pages with their types
stats       iterate over all pages and generate usage stats

Although the API of bolt is relatively simple, its implementation is relatively elegant. Many large enterprises use bolt internally to store some business data. If there are some applications with high requirements for reading, you can consider testing the effect of bolt.

In addition, it should be noted that for bolt, the stored files are memory mapped objects, so they are size sensitive, which may cause them to be copied to a specific machine and can not be used normally. However, most users use modern CPU, which is small end storage, so it is not a problem.

Topics: Go