您的位置:首页 > 编程语言 > Go语言

GorocksDB和BoltDB读写性能测试

2017-07-06 18:17 1506 查看
由于项目需求,要使用rocksdb来进行元数据metadata的存储来快速读写数据,那么使用在rocksdb上封装了go的gorocksdb还是使用完全用go实现的boltdb,需要进行测试。

一、搭建环境

安装原生的rocksdb(https://github.com/facebook/rocksdb & http://blog.jeffli.me/blog/2016/12/02/getting-started-with-rocksdb-in-centos-7/

gorocksdb安装:https://github.com/tecbot/gorocksdb

>在安装的过程中遇到stdatomic的问题,升级g++:http://blog.csdn.net/lavorange/article/details/70854459

>go build的过程中报错:

# github.com/tecbot/gorocksdb
../github.com/tecbot/gorocksdb/backup.go:4:24: fatal error: rocksdb/c.h: No such file or directory
// #include "rocksdb/c.h"
compilation terminated.
解决方法:https://github.com/tecbot/gorocksdb/issues/52
>go build -tags=embed

boltdb安装https://github.com/boltdb/bolt

二、编写benchmark

gorocksdb.go:

package main

import (
"crypto/md5"
"encoding/hex"
"fmt"
"log"
"math/rand"
"os"
"strconv"
"time"

"github.com/tecbot/gorocksdb"
)

func main() {
concurrency, _ := strconv.Atoi(os.Args[1])
tasks, _ := strconv.Atoi(os.Args[2])
disks, _ := strconv.Atoi(os.Args[3])
partitions, _ := strconv.Atoi(os.Args[4])

dbPaths := listDBPaths('b', disks, partitions)
dbs := openDBs(dbPaths)
ropt := gorocksdb.NewDefaultReadOptions()
wopt := gorocksdb.NewDefaultWriteOptions()

wch := make(chan time.Duration, concurrency)
wsuccess := make(chan int, concurrency)

rch := make(chan time.Duration, concurrency)
rsuccess := make(chan int, concurrency)
srcs := prepareData(1000)

keys := populateData(1000, srcs, dbs)

for t := 0; t < 10; t++ {
log.Printf("start to benchmark...")
begin := time.Now()
for i := 0; i < concurrency; i++ {
go func(id int) {
var rduration time.Duration
var wduration time.Duration
rsucc := 0
wsucc := 0

for j := 0; j < tasks; j++ {
di := rand.Intn(len(dbs))
db := dbs[di]

if rand.Intn(100) <= 20 {
key := generateKey()
data := srcs[rand.Intn(len(srcs))].Data

start := time.Now()
if err := db.Put(wopt, []byte(key), data); err == nil {
wduration += time.Since(start)
wsucc++
}
} else {
key := keys[di][rand.Intn(len(keys[di]))]

start := time.Now()
if _, err := db.Get(ropt, []byte(key)); err == nil {
rduration += time.Since(start)
rsucc++
}
}
}

rch <- rduration
rsuccess <- rsucc
wch <- wduration
wsuccess <- wsucc
}(i)
}

var relapsed, welapsed time.Duration
var rcount, wcount int64
for i := 0; i < concurrency; i++ {
relapsed += <-rch
welapsed += <-wch
rcount += int64(<-rsuccess)
wcount += int64(<-wsuccess)
}

d := time.Since(begin)

log.Printf("For read requests:")
log.Printf("it took %s", relapsed)
log.Printf("success requests: %d", rcount)
log.Printf("time cost per request: %.2fms", float64((relapsed/time.Millisecond))/float64(rcount))
log.Printf("qps: %.2f\n\n", float64(rcount*1000)/float64(d/time.Millisecond))
log.Printf("For write requests:")
log.Printf("it took %s", welapsed)
log.Printf("success requests: %d", wcount)
log.Printf("time cost per request: %.2fms", float64((welapsed/time.Millisecond))/float64(wcount))
log.Printf("qps: %.2f\n\n\n", float64(wcount*1000)/float64(d/time.Millisecond))

time.Sleep(time.Second * 600)
}

for _, d := range dbs {
d.Close()
}
}

func generateKey() string {
t := fmt.Sprintf("tenant%06d", rand.Intn(100))
c := fmt.Sprintf("container%04d", rand.Intn(10))

ts := time.Now()
o := strconv.FormatInt(ts.UnixNano(), 10)

return fmt.Sprintf("/%s/%s/%s", t, c, o)
}

type src struct {
Data     []byte
Checksum string
}

func prepareData(n int) []src {
var srcs []src
for i := 0; i < n; i++ {
data := Bytes(256)
checksum := md5.Sum(data)
srcs = append(srcs, src{Data: data, Checksum: hex.EncodeToString(checksum[:])})
}

return srcs
}

func openDBs(paths []string) []*gorocksdb.DB {
var dbs []*gorocksdb.DB

opts := gorocksdb.NewDefaultOptions()
opts.SetCreateIfMissing(true)
opts.SetCompression(gorocksdb.NoCompression)
opts.SetWriteBufferSize(671088640)

for _, p := range paths {
db, err := gorocksdb.OpenDb(opts, p)
if err != nil {
log.Fatal(err)
}
dbs = append(dbs, db)
}

return dbs
}

func listDBPaths(begin rune, disks int, partitions int) []string {
var dbs []string

for i := 0; i < disks; i++ {
for j := 0; j < partitions; j++ {
db := fmt.Sprintf("/srv/node/sd%c/%d", begin, j)
dbs = append(dbs, db)
}
begin += 1
}

return dbs
}

func populateData(num int, srcs []src, dbs []*gorocksdb.DB) [][]string {
keys := make([][]string, len(dbs))
wopt := gorocksdb.NewDefaultWriteOptions()
wopt.SetSync(true)

for i, db := range dbs {
var ks []string
for j := 0; j < num; j++ {
data := srcs[rand.Intn(len(srcs))].Data
p := generateKey()

if err := db.Put(wopt, []byte(p), data); err != nil {
continue
}
ks = append(ks, p)
}

keys[i] = ks
}

return keys
}

func checksum(data []byte) string {
checksum := md5.Sum(data)
return hex.EncodeToString(checksum[:])
}

func Bytes(n int) []byte {
d := make([]byte, n)
rand.Read(d)

return d
}
boltdb.go:

package main
import (
"crypto/md5"
"encoding/hex"
"fmt"
"log"
"math/rand"
"os"
"strconv"
"time"
"github.com/boltdb/bolt"
)
func main() {
concurrency, _ := strconv.Atoi(os.Args[1])
tasks, _ := strconv.Atoi(os.Args[2])
disks, _ := strconv.Atoi(os.Args[3])
partitions, _ := strconv.Atoi(os.Args[4])
dbPaths := listDBPaths('b', disks, partitions)
dbs := openDBs(dbPaths)
wch := make(chan time.Duration, concurrency)
wsuccess := make(chan int, concurrency)
rch := make(chan time.Duration, concurrency)
rsuccess := make(chan int, concurrency)
srcs := prepareData(1000)
keys := populateData(1000, srcs, dbs)
di := rand.Intn(len(dbs))
db := dbs[di]
//var bucket *bolt.Bucket
//var err error
db.Update(func(tx *bolt.Tx) error {
//bucket, err = tx.CreateBucketIfNotExists([]byte("iqiyi"))
_, err := tx.CreateBucketIfNotExists([]byte("iqiyi"))
if err != nil {
return err
}
return nil
})
for t := 0; t < 3; t++ {
log.Printf("start to benchmark...")
begin := time.Now()
for i := 0; i < concurrency; i++ {
go func(id int) {
var rduration time.Duration
var wduration time.Duration
rsucc := 0
wsucc := 0
for j := 0; j < tasks; j++ {
if rand.Intn(100) <= 20 {
key := generateKey()
data := srcs[rand.Intn(len(srcs))].Data
start := time.Now()
if err := db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("iqiyi"))
err := b.Put([]byte(key), []byte(data))
return err
}); err == nil {
wduration += time.Since(start)
wsucc++
}
} else {
key := keys[di][rand.Intn(len(keys[di]))]
start := time.Now()
if err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("iqiyi"))
_ = b.Get([]byte(key))
return nil
}); err == nil {
rduration += time.Since(start)
rsucc++
}
}
}
rch <- rduration
rsuccess <- rsucc
wch <- wduration
wsuccess <- wsucc
}(i)
}
var relapsed, welapsed time.Duration
var rcount, wcount int64
for i := 0; i < concurrency; i++ {
relapsed += <-rch
welapsed += <-wch
rcount += int64(<-rsuccess)
wcount += int64(<-wsuccess)
}
d := time.Since(begin)
log.Printf("For read requests:")
log.Printf("it took %s", relapsed)
log.Printf("success requests: %d", rcount)
log.Printf("time cost per request: %.6fms", float64((relapsed/time.Millisecond))/float64(rcount))
log.Printf("qps: %.2f\n\n", float64(rcount*1000)/float64(d/time.Millisecond))
log.Printf("For write requests:")
log.Printf("it took %s", welapsed)
log.Printf("success requests: %d", wcount)
log.Printf("time cost per request: %.6fms", float64((welapsed/time.Millisecond))/float64(wcount))
log.Printf("qps: %.2f\n\n\n", float64(wcount*1000)/float64(d/time.Millisecond))
time.Sleep(time.Second * 10)
}
for _, d := range dbs {
d.Close()
}
}
func generateKey() string {
t := fmt.Sprintf("tenant%06d", rand.Intn(100))
c := fmt.Sprintf("container%04d", rand.Intn(10))
ts := time.Now()
o := strconv.FormatInt(ts.UnixNano(), 10)
return fmt.Sprintf("/%s/%s/%s", t, c, o)
}
type src struct {
Data     []byte
Checksum string
}
func prepareData(n int) []src {
var srcs []src
for i := 0; i < n; i++ {
data := Bytes(256)
checksum := md5.Sum(data)
srcs = append(srcs, src{Data: data, Checksum: hex.EncodeToString(checksum[:])})
}
return srcs
}
func openDBs(paths []string) []*bolt.DB {
var dbs []*bolt.DB
for _, p := range paths {
db, err := bolt.Open(p, 0600, &bolt.Options{Timeout: 1 * time.Second})
if err != nil {
log.Fatal(err)
}
dbs = append(dbs, db)
}
return dbs
}
func listDBPaths(begin rune, disks int, partitions int) []string {
var dbs []string
for i := 0; i < disks; i++ {
for j := 0; j < partitions; j++ {
db := fmt.Sprintf("/srv/node/sd%c/%d", begin, j)
dbs = append(dbs, db)
}
begin += 1
}
return dbs
}
func populateData(num int, srcs []src, dbs []*bolt.DB) [][]string {
keys := make([][]string, len(dbs))
for i, db := range dbs {
var ks []string
for j := 0; j < num; j++ {
data := srcs[rand.Intn(len(srcs))].Data
p := generateKey()
if err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte("iqiyi"))
if err != nil {
return err
}
return b.Put([]byte(p), []byte(data))
}); err != nil {
continue
}
ks = append(ks, p)
}
keys[i] = ks
}
return keys
}
func checksum(data []byte) string {
checksum := md5.Sum(data)
return hex.EncodeToString(checksum[:])
}
func Bytes(n int) []byte {
d := make([]byte, n)
rand.Read(d)
return d
}

三、测试数据

写入数据大小:256Bytes

gorocksdb   readwrite
读写比concurrencytaskdiskspartitionstime cost per request(ms)qpstime cost per request(ms)qps
1/1110000110.00219266280.1475316581
     0.00266148860.1470475114
     0.00264149220.1473025078
 1100000110.00251364030.1473756573
     0.0027462850.1474316568
     0.00287363280.1472256568
 1010000110.00416663061.423326573
     0.00453862941.4217586572
     0.00505362851.4241456572
 10100000110.00447263191.4306656572
     0.00483963091.4298046572
     0.00488363221.4343866572
读写比concurrencytaskdiskspartitionstime cost per request(ms)qpstime cost per request(ms)qps
2/1110000110.001956130560.14526589
     0.002131685640.0046787846
     0.0019841771350.00464393135
 1100000110.002195127910.1454976569
     0.002455127390.1449946569
     0.0026126140.144766568
 1010000110.003442127281.416156573
     0.004326126891.4243466570
     0.003779125491.4148296570
 10100000110.003906131401.3901116776
     0.0035592588630.036561133292
     0.0037152591690.037855133295
读写比concurrencytaskdiskspartitionstime cost per request(ms)qpstime cost per request(ms)qps
4/1110000110.0018792661660.0039767166
     0.0020142336170.00486160500
     0.0020212474370.00480365062
 1100000110.0020952365670.00510861940
     0.0021562292500.00501561447
     0.0021812319730.00515962144
 1010000110.0029654580750.0146119959
     0.0033174316830.014522114765
     0.003194437350.014989118061
 10100000110.0030694467820.013929118508
     0.0031614495780.014084119573
     0.0032544474190.013919118832
         
boltdb    readwrite
读写比concurrencytaskdiskspartitionstime cost per request(ms)qpstime cost per request(ms)qps
1/1110000110.00653210650.8886491109
     0.0067110950.870721131
     0.00678310800.864071140
 1100000110.00695211500.8284351187
     0.00649813240.7153771373
     0.00645216630.5613831743
 1010000110.00670820643.7206862131
     0.00635526873.0930972786
     0.00661636222.3610143797
 10100000110.00550943982.038134579
     0.00550243942.050864576
     0.00551443752.0977174567
2/1110000110.00464592690.2032474619
     0.00469588390.2063594547
     0.00459387440.202714642
 1100000110.00458390030.2035764615
     0.00459389700.2030274627
     0.00465888910.2041774599
 1010000110.00482586761.8690944447
     0.00562482172.0510714239
     0.00490185641.9061714430
 10100000110.00503885492.1098654400
     0.0049586002.19194443
     0.00496886402.0923984457
4/1110000110.004395167990.2106044297
     0.004039170730.203084478
     0.004058164610.2066194415
 1100000110.004016172540.2006924536
     0.004095169790.203214479
     0.004084168760.2011444527
 1010000110.00466165092.1540424340
     0.00484162372.1174134283
     0.004718160222.179914298
 10100000110.004644164822.1880084370
     0.004745164252.2179734364
     0.004661164182.1966454371
四、结论

1.从读性能上来说,boltdb与gorocksdb在同一个数量级上,速度都很快,在微妙级别。

2.从写性能上来看,boltdb的写速度比gorocksdb稍慢,但是在同一个数量级。

3.在并发量扩大十倍(10个并发相比于单并发)的情况下,读性能没有影响,boltdb写入的时间也延时了一个数量级。

Author:忆之独秀
Email:leaguenew@qq.com
注明出处:http://blog.csdn.net/lavorange/article/details/74566724
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息