Tokyo Cabinet 是早期的用 C 语言写的 KV 数据库,还支持多种数据结构,主要是在一定级别的数据量时读写性能很快。
Tokyo Cabinet数据结构:
这里就使用 Tokyo Cabinet 的 Hash 数据结构,与 goleveldb、boltdb 做简单读写对比。
10 条数据测试
1
2
3
4
5
6
7
8
9
10
11
12
13
% go test -bench "Benchmark*" -benchmem -benchtime 10s
kvLst len 20
db size 7847B 0MB
goos: darwin
goarch: amd64
pkg: tk
cpu: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
BenchmarkTokyoCabinetPut-8 3570594 3318 ns/op 480 B/op 20 allocs/op
BenchmarkTokyoCabinetGet-8 1582812 7626 ns/op 8632 B/op 40 allocs/op
BenchmarkGoleveldbPut-8 68617 182042 ns/op 1920 B/op 36 allocs/op
BenchmarkGoleveldbGet-8 536542 22335 ns/op 15112 B/op 135 allocs/op
BenchmarkBoltdbPut-8 340 31076948 ns/op 8546 B/op 85 allocs/op
BenchmarkBoltdbGet-8 3678506 3162 ns/op 1112 B/op 25 allocs/op
纯数据内容长度 7847B 0MB
,数据库落地文件大小:
1
2
3
4
% du -h -d=0 ./*
64K ./bdb
528K ./kc.hdb
152K ./ldb
1万条数据测试
1
2
3
4
5
6
7
8
9
10
11
12
13
% go test -bench "Benchmark*" -benchmem -benchtime 10s
kvLst len 20000
db size 14650158B 13MB
goos: darwin
goarch: amd64
pkg: tk
cpu: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
BenchmarkTokyoCabinetPut-8 2432 4647572 ns/op 480000 B/op 20000 allocs/op
BenchmarkTokyoCabinetGet-8 1141 10363653 ns/op 15956627 B/op 40000 allocs/op
BenchmarkGoleveldbPut-8 73 453146188 ns/op 4003451 B/op 46975 allocs/op
BenchmarkGoleveldbGet-8 256 48339840 ns/op 23214270 B/op 147134 allocs/op
BenchmarkBoltdbPut-8 102 101226397 ns/op 8089536 B/op 61063 allocs/op
BenchmarkBoltdbGet-8 1807 6226045 ns/op 1680423 B/op 30005 allocs/op
纯数据内容长度 14650158B 13MB
,数据库落地文件大小:
1
2
3
4
% du -h -d=0 ./*
58M ./bdb
15M ./kc.hdb
8.1M ./ldb
10万条数据测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
% go test -bench "Benchmark*" -benchmem -benchtime 10s
kvLst len 200000
db size 155574397B 148MB
goos: darwin
goarch: amd64
pkg: tk
cpu: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
BenchmarkTokyoCabinetPut-8 30 396129652 ns/op 4800000 B/op 200000 allocs/op
BenchmarkTokyoCabinetGet-8 38 268159463 ns/op 169038873 B/op 400000 allocs/op
BenchmarkPogRebPut-8 8 1278526453 ns/op 280888388 B/op 400873 allocs/op
BenchmarkPogRebGet-8 116 100922318 ns/op 164638849 B/op 100000 allocs/op
BenchmarkGoleveldbPut-8 3 7327294795 ns/op 65032520 B/op 586448 allocs/op
BenchmarkGoleveldbGet-8 21 499298687 ns/op 242636104 B/op 1480461 allocs/op
BenchmarkBoltdbPut-8 1 42497381257 ns/op 4128697728 B/op 5152018 allocs/op
BenchmarkBoltdbGet-8 128 92002808 ns/op 16800476 B/op 300005 allocs/op
BenchmarkBBoltdbPut-8 1 35440237245 ns/op 4295830096 B/op 5409813 allocs/op
BenchmarkBBoltdbGet-8 141 81632530 ns/op 16800484 B/op 300006 allocs/op
BenchmarkPebblePut-8 2 5800454474 ns/op 39278380 B/op 21740 allocs/op
BenchmarkPebbleGet-8 21 548809606 ns/op 3295075 B/op 204732 allocs/op
纯数据内容长度 155574397B 148MB
,数据库落地文件大小:
1
2
3
4
5
6
7
% du -h -d=0 ./*
223M ./bbdb
224M ./bdb
152M ./kc.hdb
94M ./ldb
234M ./pbb
2.2G ./pogdb
新增
- https://github.com/akrylysov/pogreb
- https://github.com/etcd-io/bbolt
- https://github.com/cockroachdb/pebble
测试代码
TokyoCabinet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
func BenchmarkTokyoCabinetPut(b *testing.B) {
var db = *tokyocabinet.NewHDB()
_ = db.Open("kc.hdb", tokyocabinet.HDBOREADER|tokyocabinet.HDBOWRITER|tokyocabinet.HDBOCREAT)
defer db.Close()
b.ResetTimer()
defer b.StopTimer()
for j := 0; j < b.N; j++ {
for i := 0; i < len(kvLst); i += 2 {
err := db.Put(kvLst[i], kvLst[i+1])
if err != nil {
log.Fatal(err)
}
}
}
}
func BenchmarkTokyoCabinetGet(b *testing.B) {
var db = *tokyocabinet.NewHDB()
_ = db.Open("kc.hdb", tokyocabinet.HDBOREADER|tokyocabinet.HDBOWRITER|tokyocabinet.HDBOCREAT)
defer db.Close()
b.ResetTimer()
defer b.StopTimer()
for j := 0; j < b.N; j++ {
c := 0
for i := 0; i < len(kvLst); i += 2 {
v, err := db.Get(kvLst[i])
c++
if err != nil {
log.Fatal(err, string(kvLst[i]), " ", c)
}
if !bytes.Equal(v, kvLst[i+1]) {
log.Fatal("not exception value")
}
}
}
}
goleveldb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func BenchmarkGoleveldbGet(b *testing.B) {
db, _ := leveldb.OpenFile("ldb", nil)
defer func() {
//_ = db.CompactRange(util.Range{})
_ = db.Close()
}()
b.ResetTimer()
defer b.StopTimer()
for j := 0; j < b.N; j++ {
for i := 0; i < len(kvLst); i += 2 {
v, err := db.Get(kvLst[i], nil)
if err != nil {
log.Fatal(err)
}
if !bytes.Equal(v, kvLst[i+1]) {
log.Fatal("not exception value")
}
}
}
}
boltdb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
func BenchmarkBoltdbPut(b *testing.B) {
db, _ := bolt.Open("bdb", 0600, nil)
defer db.Close()
b.ResetTimer()
defer b.StopTimer()
_ = db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte("b"))
return err
})
for j := 0; j < b.N; j++ {
err := db.Update(func(tx *bolt.Tx) error {
bc := tx.Bucket([]byte("b"))
var er2 error
for i := 0; i < len(kvLst); i += 2 {
er2 = bc.Put(kvLst[i], kvLst[i+1])
if er2 != nil {
log.Fatal(er2)
}
}
return er2
})
if err != nil {
log.Fatal(err)
}
}
}
func BenchmarkBoltdbGet(b *testing.B) {
db, _ := bolt.Open("bdb", 0600, nil)
defer db.Close()
b.ResetTimer()
defer b.StopTimer()
_ = db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte("b"))
return err
})
for j := 0; j < b.N; j++ {
_ = db.View(func(tx *bolt.Tx) error {
bc := tx.Bucket([]byte("b"))
for i := 0; i < len(kvLst); i += 2 {
v := bc.Get(kvLst[i])
if !bytes.Equal(v, kvLst[i+1]) {
log.Fatal("not exception value")
}
}
return nil
})
}
}
结论
Tokyo Cabinet 在 2千万 这个数据级别下性能很赞,上亿以后性能就急剧下降;goleveldb 中规中矩,但数据量很大时依然稳定;boltdb 读性能一直很棒,一次事务写很多数据时有点不稳定。
关于数据库大小,Tokyo Cabinet 和 goleveldb 都用到压缩,Tokyo Cabinet 比元数据多一丁点(1%),goleveldb 是元数据的 60%,boltdb 是元数据的 1.5~4倍(空闲空间多)。
另外需要注意的是 Tokyo Cabinet 的 key 是无序,其它两个是有序。
参考
- Tokyo Cabinet http://fallabs.com/tokyocabinet/
- Tokyo Cabinet 文档 http://fallabs.com/tokyocabinet/spex-en.html
- go-tokyocabinet https://github.com/TerraTech/go-tokyocabinet
本文网址: https://golangnote.com/topic/294.html 转摘请注明来源