##key value db 中的性能怪兽--badger
###前言
对于我来说今天是个值得庆祝的日子,因为本公众号迎来了很多新的朋友。在此也希望我的原创文章
能带给你们越来越多的干货,另外回答一下老铁们比较关心的问题:“是否会有从入门到精通的教程?”,我的回答是 一定会有,但不是 入门到精通 ,而是 入门到大数据项目实战 并且最近就会整理出课表分享给大家。也希望老铁们持续关注,并给我留言转发(留言可以直接给该公众号发文字消息,因为腾讯把留言功能暂时关闭了,什么时候开放暂时不清楚)。
###简介
大数据中常用到一类工具就是 key-value db,今天我就给大家介绍一下 badger, 一个可嵌入,持久,简单,快速的键值( KV )存储,并且是用纯 Go 编写(没有 CGO )。Badger 在进行随机读取时比 RocksDB 快至少 3.5 倍。 对于 128B 到 16KB 之间的数据量,数据加载速度是 RocksDB 的 0.86x - 14 倍。
We call it Badger. Based on benchmarks, Badger is at least 3.5x faster than RocksDB when doing random reads. For value sizes between 128B to 16KB, data loading is 0.86x - 14x faster compared to RocksDB, with Badger gaining significant ground as value size increases. On the flip side, Badger is currently slower for range key-value iteration, but that has a lot of room for optimization.
###优势
Badger 的优势很明显,叫它性能怪兽是有原因的:
urthermore, during benchmarking, we found that Badger ’ s LSM tree is so small, it can easily fit in RAM. For 1KB values and 75 million 22 byte keys, the raw size of the entire dataset is 72 GB. Badger ’ s LSM tree size for this setup is a mere 1.7G, which can easily fit into RAM. This is what causes Badger ’ s random key lookup performance to be at least 3.5x faster, and Badger ’ s key-only iteration to be blazingly faster than RocksDB.
随机读取速度快
可直接嵌入应用
使用简单方便
支持多种加载模式
针对 SSD 优化加成
纯 Go 编写
基于 LSM-tree 更小巧
###劣势
劣势在官方网站上也有相关说明(如下),对于不支持集群的问题可以考虑一下 Apple 最近开源的 Foundationdb :
On the flip side, Badger is currently slower for range key-value iteration, but that has a lot of room for optimization.
迭代速度比较慢
不支持集群
不可多个应用同时操作数据库
###性能
###应用
####打开数据库
打开数据时有三种加载模式,默认模式是传统的文件 IO,:
const (
// FileIO indicates that files must be loaded using standard I/O
FileIO FileLoadingMode = iota
// LoadToRAM indicates that file must be loaded into RAM
LoadToRAM
// MemoryMap indicates that that the file must be memory-mapped
MemoryMap
)
func GetDB(path string) *badger.DB {
opts := badger.DefaultOptions
opts.Dir = path
opts.ValueDir = path
opts.TableLoadingMode = options.FileIO
db, err := badger.Open(opts)
if err != nil {
fmt.Println("Open db failed:", err.Error())
return nil
}
return db
}
####set 数据
set 数据都是以 byte 数组的形式入参,毕竟什么样的数据都可以转成[]byte,此处可以封装一下缓存来提高插入数据的效率和性能.
func Set(k []byte, v []byte) {
db.DB.Update(func(tx *badger.Txn) error {
err := tx.Set(k, v)
if err != nil {
fmt.Println("Key-value db set value failed : ", err.Error())
return err
}
return nil
})
}
####get 数据
func Get(k []byte) ([]byte, error) {
var r []byte
err := db.DB.View(func(tx *badger.Txn) error {
item, err := tx.Get(k)
if err != nil {
return err
} else {
r, err = item.Value()
if err != nil {
return err
}
}
return nil
})
return r, err
}
####遍历数据
遍历数据的效率其实也没有想象中那么差,100 万的数据,Key 的长度为 50 个随机字母,遍历完成的时间是 2 分 03 秒,我们项目上对这个速度已经是比较满意的啦。
db.View(func(txn *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.PrefetchSize = 10
it := txn.NewIterator(opts)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
item := it.Item()
k := item.Key()
v, err := item.Value()
if err != nil {
return err
}
fmt.Printf("%s key=%s, value=%s\n",time.Now().Format("2006-01-02 15:04:05"), k, v)
}
return nil
})
####Prefix
Badger 当然也支持 Prefix 扫描遍历:
db.View(func(txn *badger.Txn) error {
it := txn.NewIterator(badger.DefaultIteratorOptions)
defer it.Close()
prefix := []byte("1234")
for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
item := it.Item()
k := item.Key()
v, err := item.Value()
if err != nil {
return err
}
fmt.Printf("key=%s, value=%s\n", k, v)
}
return nil
})
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.