依赖
module go.etcd.io/bbolt
go 1.12
可以看到没有依赖任何第三方组件。main函数的参数解析逻辑都是自己写的,所以导致main.go有2k+行。其实用cobra会好一点吧。
子命令
Bolt is a tool for inspecting bolt databases.
Usage:
bolt command [arguments]
The commands are:
bench run synthetic benchmark against bolt
buckets print a list of buckets
check verifies integrity of bolt database //就是tx.check
compact copies a bolt database, compacting it in the process
dump print a hexadecimal dump of a single page
get print the value of a key in a bucket
info print basic info
keys print a list of keys in a bucket
help print this screen
page print one or more pages in human readable format
pages print list of pages with their types
page-item print the key and value of a page item.
stats iterate over all pages and generate usage stats
Use "bolt [command] -h" for more information about a command.
其实db文件就是一个按照bbolt格式存储的普通文件,这些命令其实就是操作这些文件。
bench
- 解析参数
var options BenchOptions
// Parse flagset.
fs := flag.NewFlagSet("", flag.ContinueOnError)
fs.StringVar(&options.ProfileMode, "profile-mode", "rw", "")
fs.StringVar(&options.WriteMode, "write-mode", "seq", "")
fs.StringVar(&options.ReadMode, "read-mode", "seq", "")
fs.IntVar(&options.Iterations, "count", 1000, "")
fs.IntVar(&options.BatchSize, "batch-size", 0, "")
fs.IntVar(&options.KeySize, "key-size", 8, "")
fs.IntVar(&options.ValueSize, "value-size", 32, "")
fs.StringVar(&options.CPUProfile, "cpuprofile", "", "")
fs.StringVar(&options.MemProfile, "memprofile", "", "")
fs.StringVar(&options.BlockProfile, "blockprofile", "", "")
fs.Float64Var(&options.FillPercent, "fill-percent", bolt.DefaultFillPercent, "")
fs.BoolVar(&options.NoSync, "no-sync", false, "")
fs.BoolVar(&options.Work, "work", false, "")
fs.StringVar(&options.Path, "path", "", "")
fs.SetOutput(cmd.Stderr)
if err := fs.Parse(args); err != nil {
return nil, err
}
这些参数是什么我们暂时不管,涉及到bbolt具体的实现,我们可以看完所有代码以后回过来看看。
- db文件
if options.Path == "" {
f, err := ioutil.TempFile("", "bolt-bench-")
if err != nil {
return nil, fmt.Errorf("temp file: %s", err)
}
f.Close()
os.Remove(f.Name())
options.Path = f.Name()
}
如果没有的话就生成临时文件,注意打开以后就remove了,避免了进程崩溃不释放的情况
// Remove path if "-work" is not set. Otherwise keep path.
if options.Work {
fmt.Fprintf(cmd.Stdout, "work: %s\n", options.Path)
} else {
defer os.Remove(options.Path)
}
所以可以传递-path=xxx -work来保存bench过后的文件
runWrites
ProfileMode: 控制在write、read的时候是否写CPUProfile, MemProfile,BlockProfile
WriteMode: seq, rnd,seq-nest, rnd-nest,
stopProfiling: 看来pprof必须掌握啊
runReads
从runWrite写入的数据读取。
buckets
bbolt buckets path_to_db
// Print buckets.
return db.View(func(tx *bolt.Tx) error {
return tx.ForEach(func(name []byte, _ *bolt.Bucket) error {
fmt.Fprintln(cmd.Stdout, string(name))
return nil
})
})
这里可以学到一个技巧:用golang的字面常量的时候会带来首位的换行,可以去掉,使用strings包的trim函数族
strings.TrimSpace(`
usage: bolt buckets PATH
Print a list of buckets.
`)
compact
compact其实就是遍历当前的db,搜集一个bucket里面的所有kv,然后一次commit到另外一个db里面去
dump
dump某一个page
获取page大小:
// Read 4KB chunk.
buf := make([]byte, 4096)
if _, err := io.ReadFull(f, buf); err != nil {
return 0, err
}
// Read page size from metadata.
m := (*meta)(unsafe.Pointer(&buf[PageHeaderSize]))
return int(m.pageSize), nil
这怎么看起来和c语言的做法啊,为毛不用gob