LevelDB性能测试|Golang调用LevelDB
不同方式使用压力测试
- 用ssdb,TCP连接方式调用,底层存储levelDB
- 直接调用Cgo的levelDB (必须保证串行)
- 直接调用Golang的LevelDB (必须保证串行)
开始:
go test -v -test.run="DB.*" -test.bench="DB.*" -test.count=1 -test.benchtime=3s go test -v -test.run="Raws.*" -test.bench="Raws.*" -test.count=1 -test.benchtime=3s go test -v -test.run="Normal.*" -test.bench="Normal.*" -test.count=1 -test.benchtime=3s
性能对比:
1.调用SSDB:随机读写,和顺序读写差异不大,网络延迟是主要问题。
goos: linux goarch: amd64 pkg: common/ssdb Benchmark_DBSXSSDBSET 30000 127546 ns/op Benchmark_DBSXSSDBGET 50000 118855 ns/op Benchmark_DBRandomSSDBSET 30000 128268 ns/op Benchmark_DBRandomSSDBGET 50000 119668 ns/op PASS ok common/ssdb 24.545s
2.直接调用LevelDB,每次都打开文件,导致特别慢:
goos: linux goarch: amd64 pkg: common/ssdb Benchmark_RawsSXSET 100 38748277 ns/op Benchmark_RawsSXGET 200 27432834 ns/op Benchmark_RawsRandomSET 100 39496210 ns/op Benchmark_RawsRandomGET 200 27987023 ns/op PASS ok common/ssdb 24.637s
改为只打开一次文件:
goos: linux goarch: amd64 pkg: common/ssdb Benchmark_RawsSXSET 1000000 3142 ns/op Benchmark_RawsSXGET 3000000 1856 ns/op Benchmark_RawsRandomSET 1000000 3892 ns/op Benchmark_RawsRandomGET 3000000 1467 ns/op PASS ok common/ssdb 24.073s
- 调用Golang LevelDB
goos: linux goarch: amd64 pkg: common/ssdb Benchmark_NormalSXSET 500 8888019 ns/op Benchmark_NormalSXGET 500000 6632 ns/op Benchmark_NormalRandomSET 500 9006333 ns/op Benchmark_NormalRandomGET 500000 6449 ns/op PASS ok common/ssdb 17.501s
Golang调用Cgo LevelDB
levelDB Golang实现的库性能有问题?那么Golang调用C++ levelDB库:
环境准备:
需要先升级cmake,要求版本3.9以上
wget https://cmake.org/files/v3.9/cmake-3.9.2.tar.gz tar xvf cmake-3.9.2.tar.gz cd cmake-3.9.2 ./bootstrap --prefix=/usr make sudo make install cmake --version
下载并编译levelDB C库:
git clone https://github.com/google/leveldb cd leveldb git checkout v1.20 make
动态库在:
ls out-shared/libleveldb.so.1.20
静态库在:
ls out-static/libleveldb.a
安装:
# cp leveldb header file sudo cp -r include/ /usr/include/ # cp lib to /usr/lib/ sudo cp out-shared/libleveldb.so.1.20 /usr/lib/ # create link sudo ln -s /usr/lib/libleveldb.so.1.20 /usr/lib/libleveldb.so.1 sudo ln -s /usr/lib/libleveldb.so.1.20 /usr/lib/libleveldb.so # update lib cache sudo ldconfig ls /usr/lib/libleveldb.so* # 显示下面 3 个文件即安装成功 /usr/lib/libleveldb.so.1.20 /usr/lib/libleveldb.so.1 /usr/lib/libleveldb.so
我们先用C++来写一个程序先/root/test.cc
:
#include <iostream> #include <cassert> #include <cstdlib> #include <string> // 包含必要的头文件 #include <leveldb/db.h> using namespace std; int main(void) { leveldb::DB *db = nullptr; leveldb::Options options; // 如果数据库不存在就创建 options.create_if_missing = true; // 创建的数据库在 /tmp/testdb leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db); assert(status.ok()); std::string key = "A"; std::string value = "a"; std::string get_value; // 写入 key1 -> value1 leveldb::Status s = db->Put(leveldb::WriteOptions(), key, value); // 写入成功,就读取 key:people 对应的 value if (s.ok()) s = db->Get(leveldb::ReadOptions(), "A", &get_value); // 读取成功就输出 if (s.ok()) cout << get_value << endl; else cout << s.ToString() << endl; delete db; return 0; }
静态编译:
cp out-static/libleveldb.a /root/ g++ test.cc -o test ./libleveldb.a -lpthread
动态:
g++ test.cc -o test -lpthread -lleveldb
接下来参考:https://github.com/jmhodges/levigo/blob/master/examples/comparator_example.go
下载后使用即可:
CGO_CFLAGS="-I/usr/include" CGO_LDFLAGS="-L/usr/lib" go get github.com/jmhodges/levigo
Golang调用ssdb
使用ssdb,用tcp方式请求,底层存储为levelDB.
wget --no-check-certificate https://github.com/ideawu/ssdb/archive/master.zip unzip master.zip cd ssdb-master apt install autoconf make # 将安装在 /usr/local/ssdb 目录下 sudo make install mkdir /root/ssdb cp ssdb-server /root/ssdb cp ssdb.conf /root/ssdb cd /root/ssdb mkdir var
编辑ssdb.conf
# ssdb-server config # MUST indent by TAB! # absolute path, or relative to path of this file, directory must exists work_dir = ./var pidfile = ./var/ssdb.pid server: ip: 0.0.0.0 port: 8888 # bind to public ip #ip: 0.0.0.0 # format: allow|deny: all|ip_prefix # multiple allows or denys is supported #deny: all #allow: 127.0.0.1 #allow: 192.168 # auth password must be at least 32 characters #auth: very-strong-password #readonly: yes # in ms, to log slowlog with WARN level #slowlog_timeout: 5 replication: binlog: yes # Limit sync speed to *MB/s, -1: no limit sync_speed: -1 slaveof: # to identify a master even if it moved(ip, port changed) # if set to empty or not defined, ip:port will be used. #id: svc_2 # sync|mirror, default is sync #type: sync #host: localhost #port: 8889 logger: level: debug output: /root/ssdb/log.txt rotate: size: 1000000000 leveldb: # in MB cache_size: 500 # in MB write_buffer_size: 64 # in MB/s compaction_speed: 1000 # yes|no compression: yes
启动:
# 启动主库, 此命令会阻塞住命令行 ./ssdb-server ssdb.conf # 或者启动为后台进程(不阻塞命令行) ./ssdb-server -d ssdb.conf # 停止 ssdb-server ./ssdb-server ssdb.conf -s stop # 对于旧版本 kill `cat ./var/ssdb.pid` # 重启 ./ssdb-server ssdb.conf -s restart
下载后使用:
go get -v github.com/seefan/gossdb
代码:
Golang levelDB:
package bench import ( "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/opt" "sync" ) type LevelDBClient struct { db *leveldb.DB *sync.Mutex } func (x *LevelDBClient) init(dir string) error { //os.Mkdir(dir, 777) db, err := leveldb.OpenFile(dir, nil) if err != nil { return err } x.Mutex = new(sync.Mutex) x.db = db return nil } func (x *LevelDBClient) Set(key, value []byte) error { x.Lock() defer x.Unlock() return x.db.Put(key, value, &opt.WriteOptions{NoWriteMerge: true, Sync: true}) } func (x *LevelDBClient) Get(key []byte) ([]byte, error) { x.Lock() defer x.Unlock() return x.db.Get(key, &opt.ReadOptions{DontFillCache: true}) }
Cgo levelDB:
package bench import ( "github.com/jmhodges/levigo" "sync" ) type RawLevelDBClient struct { db *levigo.DB ro *levigo.ReadOptions wb *levigo.WriteOptions *sync.Mutex } func (x *RawLevelDBClient) init(dir string) error { opts := levigo.NewOptions() //opts.SetCache(levigo.NewLRUCache(3 << 30)) opts.SetCreateIfMissing(true) db, err := levigo.Open(dir, opts) if err != nil { return err } x.Mutex = new(sync.Mutex) x.db = db x.ro = levigo.NewReadOptions() x.wb = levigo.NewWriteOptions() return nil } func (x *RawLevelDBClient) Get(key []byte) (data []byte, err error) { x.Lock() defer x.Unlock() data, err = x.db.Get(x.ro, key) if err != nil { return } return } func (x *RawLevelDBClient) Set(key []byte, value []byte) (err error) { x.Lock() defer x.Unlock() err = x.db.Put(x.wb, key, value) return }
测试文件(三种方式):
package bench import ( "testing" "github.com/seefan/gossdb" "github.com/seefan/gossdb/conf" "fmt" "math/rand" ) func Benchmark_DBSXSSDBSET(b *testing.B) { pool, err := gossdb.NewPool(&conf.Config{ Host: "192.168.153.15", Port: 8888, MinPoolSize: 5, MaxPoolSize: 50, AcquireIncrement: 5, RetryEnabled: true, }) if err != nil { fmt.Println("xxx") return } defer pool.Close() for i := 0; i < b.N; i++ { c, err := pool.NewClient() if err != nil { if c != nil { c.Close() } fmt.Println(err.Error()) return } err = c.Set(fmt.Sprintf("%d", i), fmt.Sprintf("%d", i)) if err != nil { fmt.Println(err.Error()) } if c != nil { c.Close() } } } func Benchmark_DBSXSSDBGET(b *testing.B) { pool, err := gossdb.NewPool(&conf.Config{ Host: "192.168.153.15", Port: 8888, MinPoolSize: 5, MaxPoolSize: 50, AcquireIncrement: 5, RetryEnabled: true, }) if err != nil { fmt.Println("xxx") return } defer pool.Close() for i := 0; i < b.N; i++ { c, err := pool.NewClient() if err != nil { if c != nil { c.Close() } fmt.Println(err.Error()) return } _, err = c.Get(fmt.Sprintf("%d", i)) if err != nil { fmt.Println(err.Error()) } if c != nil { c.Close() } } } func Benchmark_DBRandomSSDBSET(b *testing.B) { pool, err := gossdb.NewPool(&conf.Config{ Host: "192.168.153.15", Port: 8888, MinPoolSize: 5, MaxPoolSize: 50, AcquireIncrement: 5, RetryEnabled: true, }) if err != nil { fmt.Println("xxx") return } defer pool.Close() for i := 0; i < b.N; i++ { c, err := pool.NewClient() if err != nil { if c != nil { c.Close() } fmt.Println(err.Error()) return } dudu := uint(rand.Intn(5)) err = c.Set(fmt.Sprintf("%d", dudu), fmt.Sprintf("%d", dudu)) if err != nil { fmt.Println(err.Error()) } if c != nil { c.Close() } } } func Benchmark_DBRandomSSDBGET(b *testing.B) { pool, err := gossdb.NewPool(&conf.Config{ Host: "192.168.153.15", Port: 8888, MinPoolSize: 5, MaxPoolSize: 50, AcquireIncrement: 5, RetryEnabled: true, }) if err != nil { fmt.Println("xxx") return } defer pool.Close() for i := 0; i < b.N; i++ { c, err := pool.NewClient() if err != nil { if c != nil { c.Close() } fmt.Println(err.Error()) return } _, err = c.Get(fmt.Sprintf("%d", uint(rand.Intn(5)))) if err != nil { fmt.Println(err.Error()) } if c != nil { c.Close() } } } var ldb = new(RawLevelDBClient) var db = new(LevelDBClient) func init() { dir := "/data/leveldb" err := ldb.init(dir) if err != nil { panic(err) } dir = "/data/leveldb1" err = db.init(dir) if err != nil { panic(err) } } func Benchmark_RawsSXSET(b *testing.B) { for i := 0; i < b.N; i++ { dudu := fmt.Sprintf("%d", i) err := ldb.Set([]byte(dudu), []byte(dudu)) if err != nil { fmt.Println(err.Error()) } } } func Benchmark_RawsSXGET(b *testing.B) { for i := 0; i < b.N; i++ { dudu := fmt.Sprintf("%d", i) _, err := ldb.Get([]byte(dudu)) if err != nil { fmt.Println(err.Error()) } else { //fmt.Println(string(v)) } } } func Benchmark_RawsRandomSET(b *testing.B) { for i := 0; i < b.N; i++ { dudu := fmt.Sprintf("%d", uint(rand.Intn(5))) err := ldb.Set([]byte(dudu), []byte(dudu)) if err != nil { fmt.Println(err.Error()) } } } func Benchmark_RawsRandomGET(b *testing.B) { for i := 0; i < b.N; i++ { _, err := ldb.Get([]byte(fmt.Sprintf("%d", uint(rand.Intn(5))))) if err != nil { //fmt.Println(err.Error()) } else { //fmt.Println(string(v)) } } } func Benchmark_NormalSXSET(b *testing.B) { for i := 0; i < b.N; i++ { dudu := fmt.Sprintf("%d", i) err := db.Set([]byte(dudu), []byte(dudu)) if err != nil { //fmt.Println(err.Error()) } else { } } } func Benchmark_NormalSXGET(b *testing.B) { for i := 0; i < b.N; i++ { dudu := fmt.Sprintf("%d", i) _, err := db.Get([]byte(dudu)) if err != nil { //fmt.Println(err.Error()) } else { //fmt.Println(string(v)) } } } func Benchmark_NormalRandomSET(b *testing.B) { for i := 0; i < b.N; i++ { dudu := fmt.Sprintf("%d", uint(rand.Intn(5))) err := db.Set([]byte(dudu), []byte(dudu)) if err != nil { //fmt.Println(err.Error()) } } } func Benchmark_NormalRandomGET(b *testing.B) { for i := 0; i < b.N; i++ { _, err := db.Get([]byte(fmt.Sprintf("%d", uint(rand.Intn(5))))) if err != nil { //fmt.Println(err.Error()) } else { //fmt.Println(string(v)) } } }