专业编程基础技术教程

网站首页 > 基础教程 正文

redis存储结构体方式和pprof性能分析

ccvgpt 2024-08-14 15:37:55 基础教程 9 ℃

GoLang Redis存储结构体方式对比

pprof: 普洛夫

redis存储结构体方式和pprof性能分析

目录结构

> # cd /data/bench

> # tree

.

├── complex

│ ├── struct.go

│ └── struct_test.go

├── go.mod

├── go.sum

└── single

├── struct.go

└── struct_test.go

2 directories, 6 files

简单结构

> # cd single

> # vim struct.go

package single

import (

"bytes"

"encoding/gob"

"encoding/json"

"fmt"

"github.com/gomodule/redigo/redis"

)

var Conn = ConnectRedis()

/**测试服务连接

*/

func ConnectRedis() redis.Conn {

conn, err := redis.Dial("tcp", "127.0.0.1:6379", redis.DialPassword("guo456"))

if err != nil {

fmt.Println("连接失败", err)

return nil

}

return conn

}

var testStruct = CreateTestData(1111)

func CreateTestData(id int) *TestStruct {

return &TestStruct{

Id: id,

Name: "测试姓名",

Sex: "男",

Desc: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc1: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc2: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc3: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc4: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc5: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc6: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc7: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc8: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

}

}

type TestStruct struct {

Id int `redis:"id" json:"id"`

Name string `redis:"name" json:"name"`

Sex string `redis:"sex" json:"sex"`

Desc string `redis:"desc" json:"desc"`

Desc1 string `redis:"desc1" json:"desc1"`

Desc2 string `redis:"desc2" json:"desc2"`

Desc3 string `redis:"desc3" json:"desc3"`

Desc4 string `redis:"desc4" json:"desc4"`

Desc5 string `redis:"desc5" json:"desc5"`

Desc6 string `redis:"desc6" json:"desc6"`

Desc7 string `redis:"desc7" json:"desc7"`

Desc8 string `redis:"desc8" json:"desc8"`

}

// struct 结构体保存的三种方式

// hash类型方式

func DoHashStore(conn redis.Conn) {

//以hash类型保存

conn.Do("hmset", redis.Args{"struct1"}.AddFlat(testStruct)...)

//获取缓存

value, _ := redis.Values(conn.Do("hgetall", "struct1"))

//将values转成结构体

object := &TestStruct{}

redis.ScanStruct(value, object)

}

// Gob Encoding方式

func DoGobEncodingStore(conn redis.Conn) {

//将数据进行gob序列化

var buffer bytes.Buffer

ecoder := gob.NewEncoder(&buffer)

ecoder.Encode(testStruct)

//reids缓存数据

conn.Do("set", "struct2", buffer.Bytes())

//redis读取缓存

rebytes, _ := redis.Bytes(conn.Do("get", "struct2"))

//进行gob序列化

reader := bytes.NewReader(rebytes)

dec := gob.NewDecoder(reader)

object := &TestStruct{}

dec.Decode(object)

}

// JSON Encoding 方式

func DoJsonEncodingStore(conn redis.Conn) {

//json序列化

datas, _ := json.Marshal(testStruct)

//缓存数据

conn.Do("set", "struct3", datas)

//读取数据

rebytes, _ := redis.Bytes(conn.Do("get", "struct3"))

//json反序列化

object := &TestStruct{}

json.Unmarshal(rebytes, object)

}

struct_test.go

package single

import (

"testing"

)

const COUNT = 10000

func BenchmarkDoHash(t *testing.B) {

for i := 0; i < COUNT; i++ {

DoHashStore(Conn)

}

}

func BenchmarkDoEncodingStore(t *testing.B) {

for i := 0; i < COUNT; i++ {

DoGobEncodingStore(Conn)

}

}

func BenchmarkDoJsonEncodingStore(t *testing.B) {

for i := 0; i < COUNT; i++ {

DoJsonEncodingStore(Conn)

}

}

测试方法:

> # go test -bench=. -benchmem

goos: linux

goarch: amd64

pkg: bench/single

cpu: Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz

BenchmarkDoHash 1 1113655645 ns/op 53842344 B/op 1060047 allocs/op

BenchmarkDoEncodingStore 1 1446198754 ns/op 180337672 B/op 3010200 allocs/op

BenchmarkDoJsonEncodingStore 1 1121650284 ns/op 38250952 B/op 220152 allocs/op

PASS

ok bench/single 3.695s

执行效率 hash类型 > JSON Encoding > Gob Encoding

内存占用 JSON Encoding < hash类型 < Gob Encoding

详细测试的方法:

> # go test -bench=. -cpu=2 -benchmem -memprofile mem.out -cpuprofile cpu.out

接下来我们再来分析导致其结果的原因, 执行以下命令:

> # go tool pprof -svg cpu.out > cpu.svg

> # go tool pprof -svg mem.out > mem.svg

利用 pprof 分析样本的集合, 并生成可视报告(cpu.svg 和 mem.svg), 然后用浏览器打开cpu.svg 和 mem.svg

数组结构

由于Hash类型不支持复杂的结构体, 因为现在只对比Gob和JSON两种方式

> # cd /data/bench/complex

> # vim struct.go

package complex

import (

"bytes"

"encoding/gob"

"encoding/json"

"fmt"

"github.com/gomodule/redigo/redis"

)

var Conn = ConnectRedis()

/**测试服务连接

*/

func ConnectRedis() redis.Conn {

conn, err := redis.Dial("tcp", "127.0.0.1:6379", redis.DialPassword("guo456"))

if err != nil {

fmt.Println("连接失败", err)

return nil

}

return conn

}

var testComplexStruct = CreateComplexData(2000)

func CreateComplexData(count int) []TestStruct {

data := make([]TestStruct, 0, count)

for i := 0; i < count; i++ {

data = append(data, *CreateTestData(i))

}

return data

}

func CreateTestData(id int) *TestStruct {

return &TestStruct{

Id: id,

Name: "测试姓名",

Sex: "男",

Desc: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc1: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc2: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc3: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc4: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc5: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc6: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc7: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

Desc8: "描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述描述",

}

}

type TestStruct struct {

Id int `redis:"id" json:"id"`

Name string `redis:"name" json:"name"`

Sex string `redis:"sex" json:"sex"`

Desc string `redis:"desc" json:"desc"`

Desc1 string `redis:"desc1" json:"desc1"`

Desc2 string `redis:"desc2" json:"desc2"`

Desc3 string `redis:"desc3" json:"desc3"`

Desc4 string `redis:"desc4" json:"desc4"`

Desc5 string `redis:"desc5" json:"desc5"`

Desc6 string `redis:"desc6" json:"desc6"`

Desc7 string `redis:"desc7" json:"desc7"`

Desc8 string `redis:"desc8" json:"desc8"`

}

// 符合 struct 结构体保存的二种方式

// Gob Encoding方式

func DoComplexGobEncodingStore(conn redis.Conn) {

//序列化数组

var buffer bytes.Buffer

ecoder := gob.NewEncoder(&buffer)

ecoder.Encode(testComplexStruct)

//缓存数据

conn.Do("set", "complex3", buffer.Bytes())

//读取数据

rebytes, _ := redis.Bytes(conn.Do("get", "complex3"))

//反序列化

reader := bytes.NewReader(rebytes)

dec := gob.NewDecoder(reader)

var object []TestStruct

dec.Decode(&object)

}

// JSON Encoding 方式

func DoComplexJSONStore(conn redis.Conn) {

//序列化数组

datas, _ := json.Marshal(testComplexStruct)

//缓存数据

conn.Do("set", "complex2", datas)

//读取数据

rebytes, _ := redis.Bytes(conn.Do("get", "complex2"))

//json反序列化

var object []TestStruct

json.Unmarshal(rebytes, &object)

}

> # struct_test.go

package complex

import (

"testing"

)

const COMPLEX_COUNT = 100

func BenchmarkDoComplexGobEncoding(t *testing.B) {

for i := 0; i < COMPLEX_COUNT; i++ {

DoComplexGobEncodingStore(Conn)

}

}

func BenchmarkDoComplexJsonEncoding(t *testing.B) {

for i := 0; i < COMPLEX_COUNT; i++ {

DoComplexJSONStore(Conn)

}

}

测试方法:

> # go test -bench=. -benchmem

goos: linux

goarch: amd64

pkg: bench/complex

cpu: Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz

BenchmarkDoComplexGobEncoding 1 1449488581 ns/op 2017753800 B/op 2234189 allocs/op

BenchmarkDoComplexJsonEncoding 1 4154683030 ns/op 793233304 B/op 2403960 allocs/op

PASS

ok bench/complex 5.614s

从结果上看, JSON的内存占用还是比Gob有优势, 但在性能上Gob已经远远甩开了JSON。

详细测试:

> # go test -bench=. -cpu=2 -benchmem -memprofile mem.out -cpuprofile cpu.out

接下来我们再来分析导致其结果的原因, 执行以下命令:

> # go tool pprof -svg cpu.out > cpu.svg

> # go tool pprof -svg mem.out > mem.svg

利用 pprof 分析样本的集合, 并生成可视报告(cpu.svg 和 mem.svg), 然后用浏览器(推荐使用 Microsoft Edge)打开cpu.svg 和 mem.svg

总结:

1. hash存储方式在简单结构体效率最高, 但不支持复杂的结构体;

2. json在内存的占用是最少的, 在简单结构体效率比gob要高。另外还有优点是业务处理中json不一定需要反序列化处理, 可直接传递给前端;

3. Gob虽然在内存占用上没有优势, 但在数组结构上优势(执行效率上)已经远远超过了JSON, 复杂的结构体(数组)采用此种方式, 比如: 区块链的区块的存储方式采用 Gob;

go tool pprof ... 常见的错误

> # go tool pprof -svg cpu.out > cpu.svg

failed to execute dot. Is Graphviz installed? Error: exec: "dot": executable file not found in %PATH%

Linux 解决方法:

> # sudo yum install graphviz

Window 解决方法:

http://www.graphviz.org/download/ 下载 Graphviz, 并配置到环境变量中, 将bin目录配置到PATH。

Tags:

最近发表
标签列表