目录
1、背景
之前在压测大数据量的业务场景时,通过pprof分析cpu耗时,发现主要耗时在json序列化和反序列化上,因此为了提高程序性能,考虑用gob编码代替json编码存储数据到redis。gob是Go语言标准库中的编码库,用于将Go数据结构(如果结构体、数组、map等)编码为二进制格式,以便在网络传输或本地存储时高效传递数据。
2、gob和json对比
特性 | gob | json |
---|---|---|
速度 | 快 | 慢 |
数据体积 | 小 | 大 |
可读性 | 不可读(二进制格式) | 可读(文本格式) |
语言支持 | 仅Go | 多语言支持 |
调试 | 难调试 | 易于调试 |
用途 | Go内部通信 | 跨语言通信 |
3、go库下载
redis使用的库为:https://github.com/redis/go-redis
下载:go get github.com/redis/go-redis/v9
4、代码示例
【1】redis初始化
package conn
import (
"context"
"sync"
"time"
"GoTest/comm/logger"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
)
var (
redisDbIndexToConnMp map[int]*redis.Client //key为几号库,value为对应库的实例
redisLock sync.RWMutex //控制并发安全
)
func init() {
redisDbIndexToConnMp = make(map[int]*redis.Client)
}
// GetRedisDB
//
// @Description: 获取对应库的连接
// @param dbIndex 对应库
// @return *redis.Client 对应库的实例
func GetRedisDB(dbIndex int) *redis.Client {
redisLock.RLock()
if conn, ok := redisDbIndexToConnMp[int(dbIndex)]; ok {
redisLock.RUnlock()
return conn
}
redisLock.RUnlock()
redisLock.Lock()
defer redisLock.Unlock()
rdb := redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "xxxxxx",
DB: dbIndex,
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
MinIdleConns: 10,
PoolSize: 100,
})
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := rdb.Ping(ctx).Err(); err != nil {
logger.Error("redis ping error", zap.Error(err))
return rdb
}
redisDbIndexToConnMp[dbIndex] = rdb
return rdb
}
封装一个在使用redis操作时初始化实例的方法,只会初始化一次。
【2】封装gob编码和解码方法
package coder
import (
"bytes"
"encoding/gob"
)
// EncodeGob
//
// @Description: gob编码
// @param date 要编码的数据结构
// @return []byte 编码之后的数据
// @return error
func EncodeGob(date interface{}) ([]byte, error) {
var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(date); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// DecodeGob
//
// @Description: gob解码
// @param gobBytes gob编码之后的数据
// @param date 接受gob解码的数据结构
// @return error
func DecodeGob(gobBytes []byte, date interface{}) error {
buf := bytes.NewBuffer(gobBytes)
if err := gob.NewDecoder(buf).Decode(date); err != nil {
return err
}
return nil
}
封装之后使用起来和json的使用方法类似。
【3】定义gob编码和解码的数据结构
package types
import "encoding/gob"
// 注册未知类型
func init() {
gob.Register(map[string]int{})
gob.Register(map[string]string{})
}
type TestGob struct {
A int
B string
C []int
D []string
E interface{} //等会使用map[string]int{}类型测试
F interface{} //等会使用map[string]string{}类型测试
}
对于interface{}对应的未知类型,需要调用gob.Register()函数去注册,不然gob解码时会识别不出是什么类型,全局初始化一次就好。
【4】gob编码
//要gob编码的数据
a := types.TestGob{
A: 111,
B: "bbb",
C: []int{1, 2, 3},
D: []string{"a", "b", "c"},
E: map[string]string{"A": "a"},
F: map[string]int{"A": 1},
}
//gob编码
gobBytes, err := coder.EncodeGob(a)
if err != nil {
logger.Error("encode gob error", zap.Error(err))
return
}
logger.Info("encode gob success")
//将编码后的数据写入redis
gobKey := "test_gob_key"
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err = conn.GetRedisDB(2).Set(ctx, gobKey, gobBytes, 0).Err(); err != nil {
logger.Error("redis set error", zap.Error(err))
return
}
logger.Info("redis set success", zap.String("key", gobKey), zap.Any("set_value", a))
上述伪代码的作用是将gob编码的数据以string类型存储到redis。代码执行控制台输出:
[2024-10-12 16:35:29.396] | INFO | Goroutine:1 | [gob_demo/main.go:38] | encode gob success
[2024-10-12 16:35:29.398] | INFO | Goroutine:1 | [gob_demo/main.go:50] | redis set success | {"key": "test_gob_key", "set_value": {"A":111,"B":"bbb","C":[1,2,3],"D":["a","b","c"],"E":{"A":"a"},"F":{"A":1}}}
【5】gob解码
//从redis中取出gob编码的数据
gobKey := "test_gob_key"
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
val, err := conn.GetRedisDB(2).Get(ctx, gobKey).Bytes()
if err != nil {
logger.Error("redis get error", zap.Error(err))
return
}
logger.Info("redis get success", zap.String("key", gobKey))
//gob解码
var b types.TestGob
if err = coder.DecodeGob(val, &b); err != nil {
logger.Error("decode gob error", zap.Error(err))
return
}
logger.Info("decode gob success", zap.Any("data", b))
}
上述伪代码的作用就是从redis中取出gob编码的数据,然后进行gob界面并打印。代码执行控制台输出:
[2024-10-12 16:35:31.968] | INFO | Goroutine:1 | [gob_demo/main.go:65] | redis get success | {"key": "test_gob_key"}
[2024-10-12 16:35:31.968] | INFO | Goroutine:1 | [gob_demo/main.go:74] | decode gob success | {"data": {"A":111,"B":"bbb","C":[1,2,3],"D":["a","b","c"],"E":{"A":"a"},"F":{"A":1}}}
5、总结
在工作中修改gob编码方式存储在redis之后,在数据量比较大的时候,相比json可以节省1/3的内存,序列化和反序列化速度更快,能提高程序性能。
标签:编码,return,string,err,redis,gob,Golang From: https://blog.csdn.net/qq_45795794/article/details/142881869