Go语言LevelDB的实现我们使用 github.com/syndtr/goleveldb/leveldb 包,通过go get命令下载该包后在程序中导入。
goleveldb主要有Get()
,Put()
等方法,可进行key/value的读取和写入,可进行事务批量Put()
插入key,Delete()
删除某个key。
package main
import (
"fmt"
"strconv"
"crypto/md5"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util"
)
var md = md5.New()
// 测试专用
func Read(db *leveldb.DB, num int) {
var kStr string
var haskKey string
kStr = strconv.Itoa(num)
md.Write([]byte(kStr))
haskKey = fmt.Sprintf("%x", md.Sum(nil))
md.Reset()
db.Get([]byte(haskKey), nil)
}
// 测试专用
func Write(db *leveldb.DB, num int) {
var kStr string
var haskKey string
kStr = strconv.Itoa(num)
md.Write([]byte(kStr))
haskKey = fmt.Sprintf("%x", md.Sum(nil))
md.Reset()
db.Put([]byte(haskKey), []byte(kStr), nil)
}
func main() {
// 打开数据库文件 /path/to/db ,第一个参数为存放数据的目录,不是具体文件
// o := &opt.Options{ Filter: filter.NewBloomFilter(10),}
// OpenFile第2个参数这里指定为nil,在数据集大时可设置比如布隆过滤器。
// *opt.Options 为nil默认为false ,true为只读模式ReadOnly
db, _ := leveldb.OpenFile("levdb", nil)
defer db.Close()
// 读数据库:Get(key,nil),写数据库:Put(key,value,nil)
// Put第三个参数为nil,默认就好,默认时写的时候如果机器崩了数据会丢失。
// key和value都是字节slice
_ = db.Put([]byte("key1"), []byte("好好检查"), nil)
_ = db.Put([]byte("key2"), []byte("天天向上"), nil)
_ = db.Put([]byte("key:3"), []byte("就会一个本事"), nil)
_ = db.Put([]byte("uname"), []byte("Jim"), nil)
_ = db.Put([]byte("time"), []byte("1450932202"), nil)
// 读数据库:Get(key,nil),返回字节slice
data, _ := db.Get([]byte("key1"), nil)
fmt.Println("key1=>", string(data))
// 删除某个key(key,nil),key不存在时并不返回错误
_ = db.Delete([]byte("key"), nil)
//迭代数据库内容:
iter := db.NewIterator(nil, nil)
fmt.Println("迭代所有key/value")
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Println(string(key), "=>", string(value))
}
iter.Release()
iter.Error()
//Seek()定位到比给定key值(字节值)要大的第一个key,可next迭代所有筛选出的key/value:
iter = db.NewIterator(nil, nil)
fmt.Println("\nSeek()按值筛选查找key")
for ok := iter.Seek([]byte("t")); ok; ok = iter.Next() {
// Use key/value.
fmt.Println("Seek-then-Iterate:")
fmt.Println(string(iter.Key()), "=>", string(iter.Value()))
}
iter.Release()
//迭代内容子集:start表示key中包含有的字符串, Limit表示key不能包含有字符串
fmt.Println("\n 按照指定(排除)条件筛选key")
iter = db.NewIterator(&util.Range{Start: []byte("key"), Limit: []byte("no")}, nil)
for iter.Next() {
// Use key/value.
fmt.Println("Iterate over subset of database content:")
fmt.Println(string(iter.Key()), "=>", string(iter.Value()))
}
iter.Release()
//迭代子集内容,key的前缀是指定字符串:
fmt.Println("\n 查找指定前缀key")
iter = db.NewIterator(util.BytesPrefix([]byte("key")), nil)
for iter.Next() {
// Use key/value.
fmt.Println("Iterate over subset of database content with a particular prefix:")
fmt.Println(string(iter.Key()), "=>", string(iter.Value()))
}
iter.Release()
_ = iter.Error()
//批量写:
batch := new(leveldb.Batch)
var kStr string
var batchkey string
for i := 0; i < 10; i++ {
kStr = strconv.Itoa(i)
md.Write([]byte(kStr))
batchkey = fmt.Sprintf("%x", md.Sum(nil))
batch.Put([]byte(batchkey), []byte(kStr))
}
md.Reset()
batch.Delete([]byte("lazy"))
_ = db.Write(batch, nil)
}
Leveldb比较突出的问题是在读操作上,在大量key的情况下可能成为性能的瓶颈,我们可以根据场景来选择使用。下面是我们进行的几种数量级别的基准测试数据:
- BenchmarkWrite-4 100000 14541 ns/op BenchmarkRead-4 100000 13094 ns/op
- BenchmarkWrite-4 500000 12724 ns/op BenchmarkRead-4 500000 17002 ns/op
- BenchmarkWrite-4 1000000 13355 ns/op BenchmarkRead-4 1000000 20610 ns/op
- BenchmarkWrite-4 3000000 15644 ns/op BenchmarkRead-4 3000000 22742 ns/op
我们可以看到随着key的数量的增加,读的性能明显地下降,而写的性能则不受影响。
下一节:Go语言BoltDB的实现我们使用 github.com/boltdb/bolt 包,通过go get命令下载该包后在程序中导入。