GolangNote

Golang笔记

在 Golang 中使用 Tokyo Cabinet Key/Value数据库

Permalink

Tokyo Cabinet 是早期的用 C 语言写的 KV 数据库,还支持多种数据结构,主要是在一定级别的数据量时读写性能很快。

Tokyo Cabinet

Tokyo Cabinet数据结构:

Tokyo Cabinet数据结构

这里就使用 Tokyo Cabinet 的 Hash 数据结构,与 goleveldb、boltdb 做简单读写对比。

10 条数据测试

Bash: 10 条数据性能 Benchmark
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,数据库落地文件大小:

Bash: 10 条数据数据库大小
1
2
3
4
% du -h  -d=0 ./*                                     
64K	./bdb
528K	./kc.hdb
152K	./ldb

1万条数据测试

Bash: 1万条数据Benchmark
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,数据库落地文件大小:

Bash: 1万条数据大小
1
2
3
4
% du -h  -d=0 ./*                                     
 58M	./bdb
 15M	./kc.hdb
8.1M	./ldb

10万条数据测试

Bash: 10万条数据Benchmark
1
2
3
4
5
6
7
8
9
10
11
12
13
% go test -bench "Benchmark*" -benchmem -benchtime 10s
kvLst len 200000
db size 154026354B 146MB
goos: darwin
goarch: amd64
pkg: tk
cpu: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
BenchmarkTokyoCabinetPut-8   	      33	 353047212 ns/op	 4800000 B/op	  200000 allocs/op
BenchmarkTokyoCabinetGet-8   	      43	 247528234 ns/op	167481822 B/op	  400000 allocs/op
BenchmarkGoleveldbPut-8      	       3	7956023630 ns/op	64237784 B/op	  585691 allocs/op
BenchmarkGoleveldbGet-8      	      24	 478244166 ns/op	240414876 B/op	 1478027 allocs/op
BenchmarkBoltdbPut-8         	       1	35963353562 ns/op	3928988728 B/op	 4990126 allocs/op
BenchmarkBoltdbGet-8         	     140	  83254538 ns/op	16800468 B/op	  300005 allocs/op

纯数据内容长度 154026354B 146MB,数据库落地文件大小:

Bash: 10万条数据大小
1
2
3
4
% du -h  -d=0 ./*
221M	./bdb
150M	./kc.hdb
 87M	./ldb

测试代码

TokyoCabinet

Go: TokyoCabinet 数据库 Put/Get 测试
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
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

Go: goleveldb 数据库 Put/Get 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

Go: boltdb 数据库 Put/Get 测试
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 是无序,其它两个是有序。

参考

本文网址: https://golangnote.com/topic/294.html 转摘请注明来源

Related articles

golang Selenium WebDriver 使用记录

Selenium WebDriver 直接通过浏览器自动化的本地接口来调用浏览器,以达到模拟浏览器行为的操作,如点击、选择、鼠标移动等。下面是记录个人使用golang 驱动的记录。...

golang snappy 的使用场合

google 自家的 snappy 压缩优点是非常高的速度和合理的压缩率。压缩率比 gzip 小,CPU 占用小。...

谷歌翻译的 golang 库推荐

Google 的翻译越来越好了,官方的Golang SDK 已经很完美,这里介绍的是几个非官方发布的有特色的库。...

Write a Comment to "在 Golang 中使用 Tokyo Cabinet Key/Value数据库"

Submit Comment Login
Based on Golang + fastHTTP + sdb | go1.17.1 Processed in 2ms