LevelDB性能测试|Golang调用LevelDB

不同方式使用压力测试

  1. 用ssdb,TCP连接方式调用,底层存储levelDB
  2. 直接调用Cgo的levelDB (必须保证串行)
  3. 直接调用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
  1. 调用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))
}
}
}

LevelDB性能测试|Golang调用LevelDB的更多相关文章

  1. JAVA中调用LevelDB用于Linux和Window环境下快速存储KV结构

    一.简介 JAVA中调用LevelDB用于Linux和Window环境下快速存储KV结构 二.依赖 <!-- https://mvnrepository.com/artifact/org.fus ...

  2. golang调用c++的dll库文件

    最近使用golang调用c++的dll库文件,简单了解了一下,特作此笔记:一.DLL 的编制与具体的编程语言及编译器无关 dll分com的dll和动态dll,Com组件dll:不管是何种语言写的都可以 ...

  3. 全面总结: Golang 调用 C/C++,例子式教程

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

  4. Golang调用windows下的dll动态库中的函数

    Golang调用windows下的dll动态库中的函数 使用syscall调用. package main import ( "fmt" "syscall" & ...

  5. golang生成c-shared so供c语言或者golang调用到例子

    1.golang生成c-shared类型到so 建立文件夹hello,创建main.go文件,内容如下 package main import "C" func main() {} ...

  6. Golang调用Python

    https://yq.aliyun.com/articles/117329 Python是时髦的机器学习御用开发语言,Golang是大红大紫的新时代后端开发语言.Python很适合让搞算法的写写模型, ...

  7. Golang 调用 C/C++,例子式教程

    大部分人学习或者使用某样东西,喜欢在直观上看到动手后的结果,才会有继续下去的兴趣. 前言: Golang 调用 C/C++ 的教程网上很多,就我目前所看到的,个人见解就是比较乱,坑也很多.希望本文能在 ...

  8. golang调用c动态库

    golang调用c动态库 简介 golang调用c语言动态库,动态方式调用,可指定动态库路径,无需系统目录下 核心技术点 封装c动态库 go语言调用c代码 实例代码 封装c动态库 头文件 test_s ...

  9. Golang 调用 aws-sdk 操作 S3对象存储

    Golang 调用 aws-sdk 操作 S3对象存储 前言 因为业务问题,要写一个S3对象存储管理代码,由于一直写Go,所以这次采用了Go,Go嘛,快,自带多线程,这种好处就不用多说了吧. 基础的功 ...

随机推荐

  1. CDH集群安装

    1 软硬件准备 1.1 准备三个节点 序号 主机名 内存 CPU IP地址 角色 1 cdh1 8G 8核心 192.168.5.78 cloudera-scm-server,mysql 2 cdh2 ...

  2. SpringBoot定时任务,总有一款适合你

    title: SpringBoot定时任务,总有一款适合你 date: 2019-09-28 16:19:10 tags: - springboot - 定时任务 categories: java - ...

  3. 解决行内块元素(inline-block)之间的空格或空白问题

    一.问题产生 由于html代码格式化后,标签会缩进或者换行.由于浏览器默认处理导致元素在页面显示中出现单个空格问题,尤其在行内或者行内块元素布局时影响比较明显 例如: 代码 页面显示 二.解决方案 这 ...

  4. Python基础(十二)

    今日主要内容 推导式 生成器表达式 lambda匿名函数 内置函数介绍 一.推导式 (一)列表推导式 先来看一段代码 建立一个空列表,向空列表中添加元素 lst = list() for i in r ...

  5. 国庆佳节第四天,谈谈我月收入增加 4K 的故事

    01.起承 在我下定决心改变的这将近 1 年的时间里,遇到了很多很多有故事的人,以及有趣的事.自我的认知改变特别大!尤其是收入,比去年同时期增加了 4K. 4K,可能也就买 100 斤猪肉.但是对于身 ...

  6. Windows Terminal 安装及美化

    windows terminal 是今年微软Build大会上推出的一款的全新终端,用来代替cmder之类的第三方终端.具有亚克力透明.多标签.Unicode支持(中文,Emoji).自带等宽字体等这些 ...

  7. .Net Core 商城微服务项目系列(十三):搭建Log4net+ELK+Kafka日志框架

    之前是使用NLog直接将日志发送到了ELK,本篇将会使用Docker搭建ELK和kafka,同时替换NLog为Log4net. 一.搭建kafka 1.拉取镜像 //下载zookeeper docke ...

  8. 01、JDBC连接

    ① 下载JDBC-MySQL数据库驱动 链接:https://pan.baidu.com/s/1KtSZZ5hXlxu2QrmsXg3lkA 提取码:1pbl ② 加载JDBC-MySQL数据库驱动 ...

  9. 面试题解析|ACL权限控制机制

    ACL(Access Control List)访问控制列表 包括三个方面: 一.权限模式(Scheme) 1.IP:从 IP 地址粒度进行权限控制 2.Digest:最常用,用类似于 usernam ...

  10. Ionic2优于Ionic1的6个理由

    经历了一个从0到有的app的开发,我已经很熟悉Ionic1了,在此期间我曾发现过Ionic1的一些bug,和一些不合理的地方(根基版本 不同,后续我会陆续发表这些文章),我甚至在此期间对Ionic1进 ...