简介

go语言中文文档:www.topgoer.com

转载:https://studygolang.com/articles/27391#reply0

buntdbbuntdb

感谢@kiyonlin推荐!

快速使用

先安装:

$ go get github.com/tidwall/buntdb

后使用:

package main

import (
  "fmt"
  "log"

  "github.com/tidwall/buntdb"
)

func main() {
  db, err := buntdb.Open(":memory:")
  if err != nil {
    log.Fatal(err)
  }
  defer db.Close()

  db.Update(func(tx *buntdb.Tx) error {
    oldValue, replaced, err := tx.Set("testkey", "testvalue", nil)
    if err != nil {
      return err
    }

    fmt.Printf("old value:%q replaced:%t\n", oldValue, replaced)
    return nil
  })

  db.View(func(tx *buntdb.Tx) error {
    value, err := tx.Get("testkey")
    if err != nil {
      return err
    }

    fmt.Println("value is:", value)
    return nil
  })
}
buntdbsqlitebuntdbbuntdb.Open():memory:buntdb
buntdbdb.View()func (tx *buntdb.Tx) errordb.View()txtxtxGet()
db.View(func(tx *buntdb.Tx) error {
  value, err := tx.Get("testkey")
  if err != nil {
    return err
  }

  fmt.Println("value is:", value)
  return nil
})
db.Update()func (tx *buntdb.Tx) errortxSettx.Set()Set()truedb.Update()

如果运行两次上面的程序,我们会看到下面的输出:

// 第一次运行
$ go run main.go 
old value:"" replaced:false
value is: testvalue

// 第二次运行
$ go run main.go 
old value:"testvalue" replaced:true
value is: testvalue

注意:

db.View()db.Update()dbErrNotFound
遍历
buntdbdb.View()buntdbAscend()
func (tx *Tx) Ascend(index string, iterator func(key, value string) bool) error
Ascend()iteratoriteratorfalse
func main() {
  db, err := buntdb.Open(":memory:")
  if err != nil {
    log.Fatal(err)
  }
  defer db.Close()

  db.Update(func(tx *buntdb.Tx) error {
    data := map[string]string{
      "a": "apple",
      "b": "banana",
      "p": "pear",
      "o": "orange",
    }
    for key, value := range data {
      tx.Set(key, value, nil)
    }
    return nil
  })

  db.View(func(tx *buntdb.Tx) error {
    var count int
    tx.Ascend("", func(key, value string) bool {
      fmt.Printf("key:%s value:%s\n", key, value)
      count++
      if count >= 3 {
        return false
      }
      return true
    })
    return nil
  })
}
""iteratorfalse
key:a value:apple
key:b value:banana
key:o value:orange
索引
buntdbdb.CreateIndex()
func (db *DB) CreateIndex(name, pattern string, less ...func(a, b string) bool) error
namepattern*user:*:nameuser::namelessbuntdbIndexStringIndexInt/IndexUint/IndexFloat
func main() {
  db, err := buntdb.Open(":memory:")
  if err != nil {
    log.Fatal(err)
  }
  defer db.Close()

  db.CreateIndex("names", "user:*:name", buntdb.IndexString)
  db.Update(func(tx *buntdb.Tx) error {
    tx.Set("user:1:name", "tom", nil)
    tx.Set("user:2:name", "Randi", nil)
    tx.Set("user:3:name", "jane", nil)
    tx.Set("user:4:name", "Janet", nil)
    tx.Set("user:5:name", "Paula", nil)
    tx.Set("user:6:name", "peter", nil)
    tx.Set("user:7:name", "Terri", nil)
    return nil
  })

  db.View(func(tx *buntdb.Tx) error {
    tx.Ascend("names", func(key, value string) bool {
      fmt.Printf("%s: %s\n", key, value)
      return true
    })
    return nil
  })
}
user:*:namenamesbuntdb.IndexStringbuntdbAscend()namesuser:*:name
user:*:name*user:*
buntdb
func main() {
  db, err := buntdb.Open(":memory:")
  if err != nil {
    log.Fatal(err)
  }
  defer db.Close()

  db.CreateIndex("ages", "user:*:age", buntdb.IndexInt)
  db.Update(func(tx *buntdb.Tx) error {
    tx.Set("user:1:age", "16", nil)
    tx.Set("user:2:age", "35", nil)
    tx.Set("user:3:age", "24", nil)
    tx.Set("user:4:age", "32", nil)
    tx.Set("user:5:age", "25", nil)
    tx.Set("user:6:age", "28", nil)
    tx.Set("user:7:age", "31", nil)
    return nil
  })

  db.View(func(tx *buntdb.Tx) error {
    tx.Ascend("ages", func(key, value string) bool {
      fmt.Printf("%s: %s\n", key, value)
      return true
    })
    return nil
  })
}
user:*:ageagesIndexInt
JSON 索引
buntdbbuntdbbuntdb.IndexJSON()name.firstcontact.email
func main() {
  db, _ := buntdb.Open(":memory:")
  defer db.Close()

  db.CreateIndex("first_name", "user:*", buntdb.IndexJSON("name.first"))
  db.CreateIndex("age", "user:*", buntdb.IndexJSON("age"))
  db.Update(func(tx *buntdb.Tx) error {
    tx.Set("user:1", `{"name":{"first":"zhang","last":"san"},"age":18}`, nil)
    tx.Set("user:2", `{"name":{"first":"li","last":"si"},"age":27`, nil)
    tx.Set("user:3", `{"name":{"first":"wang","last":"wu"},"age":32}`, nil)
    tx.Set("user:4", `{"name":{"first":"sun","last":"qi"},"age":8}`, nil)
    return nil
  })

  db.View(func(tx *buntdb.Tx) error {
    fmt.Println("Order by first name")
    tx.Ascend("first_name", func(key, value string) bool {
      fmt.Printf("%s: %s\n", key, value)
      return true
    })

    fmt.Println("Order by age")
    tx.Ascend("age", func(key, value string) bool {
      fmt.Printf("%s: %s\n", key, value)
      return true
    })

    fmt.Println("Order by age range 18-30")
    tx.AscendRange("age", `{"age":18}`, `{"age":30}`, func(key, value string) bool {
      fmt.Printf("%s: %s\n", key, value)
      return true
    })
    return nil
  })
}
user:
name.firstagename.firstageAscendRangeAscendAscendRangeminmax[min, max)max
多重索引
CreateIndex()first_nameagename.firstage
func main() {
  db, _ := buntdb.Open(":memory:")
  defer db.Close()

  db.CreateIndex("first_name_age", "user:*", buntdb.IndexJSON("name.first"), buntdb.IndexJSON("age"))
  db.Update(func(tx *buntdb.Tx) error {
    tx.Set("user:1", `{"name":{"first":"zhang","last":"san"},"age":18}`, nil)
    tx.Set("user:2", `{"name":{"first":"li","last":"si"},"age":27`, nil)
    tx.Set("user:3", `{"name":{"first":"wang","last":"wu"},"age":30}`, nil)
    tx.Set("user:4", `{"name":{"first":"sun","last":"qi"},"age":8}`, nil)
    tx.Set("user:5", `{"name":{"first":"li", "name":"dajun"},"age":20}`, nil)
    return nil
  })

  db.View(func(tx *buntdb.Tx) error {
    tx.Ascend("first_name_age", func(key, value string) bool {
      fmt.Printf("%s: %s\n", key, value)
      return true
    })
    return nil
  })
}
user:2user:5name.firstliageuser:5user:2
降序
buntdb.Desc()buntdb.IndexIntbuntdb.Desc(buntdb.IndexInt)
func main() {
  db, _ := buntdb.Open(":memory:")
  defer db.Close()

  db.CreateIndex("ages", "user:*:age", buntdb.Desc(buntdb.IndexInt))
  db.Update(func(tx *buntdb.Tx) error {
    tx.Set("user:1:age", "16", nil)
    tx.Set("user:2:age", "35", nil)
    tx.Set("user:3:age", "24", nil)
    tx.Set("user:4:age", "32", nil)
    tx.Set("user:5:age", "25", nil)
    tx.Set("user:6:age", "28", nil)
    tx.Set("user:7:age", "31", nil)
    return nil
  })

  db.View(func(tx *buntdb.Tx) error {
    tx.Ascend("ages", func(key, value string) bool {
      fmt.Printf("%s: %s\n", key, value)
      return true
    })
    return nil
  })
}
过期
buntdbbuntdb.SetOptionsbuntdbnil
func main() {
  db, _ := buntdb.Open(":memory:")
  defer db.Close()

  db.Update(func(tx *buntdb.Tx) error {
    tx.Set("testkey", "testvalue", &buntdb.SetOptions{Expires: true, TTL: time.Second})
    return nil
  })

  db.View(func(tx *buntdb.Tx) error {
    value, _ := tx.Get("testkey")
    fmt.Println("value is:", value)
    return nil
  })

  time.Sleep(time.Second)

  db.View(func(tx *buntdb.Tx) error {
    value, _ := tx.Get("testkey")
    fmt.Println("value is:", value)
    return nil
  })
}
1sSleep
value is: testvalue
value is: 
杂项遍历时删除
buntdb
func main() {
  db, _ := buntdb.Open(":memory:")
  defer db.Close()

  db.Update(func(tx *buntdb.Tx) error {
    tx.Set("user:1:age", "16", nil)
    tx.Set("user:2:age", "35", nil)
    tx.Set("user:3:age", "24", nil)
    tx.Set("user:4:age", "32", nil)
    tx.Set("user:5:age", "25", nil)
    tx.Set("user:6:age", "28", nil)
    tx.Set("user:7:age", "31", nil)
    return nil
  })

  db.Update(func(tx *buntdb.Tx) error {
    // 先汇总
    deleteKeys := make([]string, 0)
    tx.Ascend("", func(key, value string) bool {
      age, _ := strconv.ParseUint(value, 10, 64)
      if age >= 30 {
        deleteKeys = append(deleteKeys, key)
      }
      return true
    })

    // 再删除
    for _, key := range deleteKeys {
      tx.Delete(key)
    }
    return nil
  })

  db.View(func(tx *buntdb.Tx) error {
    tx.Ascend("", func(key, value string) bool {
      fmt.Printf("%s: %s\n", key, value)
      return true
    })
    return nil
  })
}
Web 服务
buntdbbuntdb
package main

import (
  "encoding/json"
  "fmt"
  "log"
  "net/http"
  "strconv"
  "time"

  "github.com/tidwall/buntdb"
)

var db *buntdb.DB

func init() {
  var err error
  db, err = buntdb.Open("data.db")
  if err != nil {
    log.Fatal(err)
  }
}

func response(w http.ResponseWriter, err error, data interface{}) {
  bytes, _ := json.Marshal(map[string]interface{}{
    "error": err,
    "data":  data,
  })
  w.Write(bytes)
}

func set(w http.ResponseWriter, r *http.Request) {
  key := r.FormValue("key")
  value := r.FormValue("value")
  expire, _ := strconv.ParseBool(r.FormValue("expire"))
  ttl, _ := time.ParseDuration(r.FormValue("ttl"))

  var setOption *buntdb.SetOptions
  if expire && ttl > 0 {
    setOption = &buntdb.SetOptions{Expires: true, TTL: ttl}
  }

  err := db.Update(func(tx *buntdb.Tx) error {
    _, _, err := tx.Set(key, value, setOption)
    return err
  })

  response(w, err, nil)
}

func get(w http.ResponseWriter, r *http.Request) {
  key := r.FormValue("key")

  var value string
  err := db.View(func(tx *buntdb.Tx) error {
    var err error
    value, err = tx.Get(key)
    return err
  })

  response(w, err, value)
}

type Pair struct {
  Key   string
  Value string
}

func iterate(w http.ResponseWriter, r *http.Request) {
  index := r.FormValue("index")
  fmt.Println(index)

  var items []Pair
  err := db.View(func(tx *buntdb.Tx) error {
    err := tx.Ascend(index, func(key, value string) bool {
      fmt.Println(key, value)
      items = append(items, Pair{key, value})
      return true
    })
    return err
  })

  response(w, err, items)
}

func createIndex(w http.ResponseWriter, r *http.Request) {
  name := r.FormValue("name")
  pattern := r.FormValue("pattern")
  less := buntdb.IndexString

  err := db.CreateIndex(name, pattern, less)
  response(w, err, nil)
}

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/get", get)
  mux.HandleFunc("/set", set)
  mux.HandleFunc("/iterate", iterate)
  mux.HandleFunc("/create_index", createIndex)

  server := &http.Server{
    Addr:    ":8000",
    Handler: mux,
  }

  if err := server.ListenAndServe(); err != nil {
    log.Fatal(err)
  }
}

我只编写了基本读取、设置、创建索引和遍历的功能,代码并不难理解。下面我们先运行程序,然后用浏览器请求:

localhost:8000/set?key=name&value=dj
{"error":null, "data":null}
errornull
localhost:8000/set?key=dj&value=18
{"error":null, "data":null}
localhost:8000/iterate
{
  "data": [
    {
      "Key": "age",
      "Value": "18"
    },
    {
      "Key": "name",
      "Value": "dj"
    }
  ],
  "error": null
}

感兴趣可以试着添加更多的功能。如果对 Go Web 编程不太了解,可以去看看我的Go Web 编程系列文章。