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 updated 2022
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,数据库落地文件大小:

Bash: 10万条数据大小
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

新增

测试代码

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
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

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
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

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实现简单的Socks5代理

Socks5 代理较 `http/https` 代理有较好的性能,下面是借鉴某个著名开源软件的 local 实现的简单代理。...

Golang http IPv4/IPv6 服务

Golang 的网络服务,如果不指定IPv4 或 IPv6,如果VPS 同时支持 IPv4 和 IPv6,`net.Listen()` 只会监听 IPv6 地址。但这不影响客户端使用 IPv4 地址来访问。...

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

Submit Comment Login
Based on Golang + fastHTTP + sdb | go1.22.3 Processed in 26ms