今天要来换个redis写法, 看看能不能缩短执行时间。
首先我会用git worktree
再开一个专案资料夹, 新资料夹就留在调整前, 称为v1版, 再修改版本就是v2版。
git worktree 可以让本机同时有两个分支相互比较或进行开发, 有时候A分支功能写到一半突然收到需求要改B分支的时候, 就需要先把分支commit或是做git stash, 後来改用worktree就可以直接再开一个资料夹对分支进行修改, 不会影响原本的开发进度; 另外像是这次要比较v1, v2 版本差异的时候, 就可以启动两个不同分支的版本进行比较。
使用 git worktree 开启另一个专案
git worktree add -b ba_master ../coconut_2
修改 redis/limit.go
第二版采用 redis支援的lua-script方式修改, 写好的整串lua-script是采原子式的方式排队执行的, 要等到前一句script执行完才会执行下一个, 它可以保证script里面的语法都执行完了才开始新的一组指令, 这在我们专案里面很常使用到。
type LimitSetting struct {
Level1 int
Level2 int
Level3 int
}
// set point
func PointSetBatch(conn *redis.Client, keys []string, point int, limitSetting map[string]int, expired int) (err error) {
// LimitSetting , lua-script json decode 使用
tmp := &LimitSetting{
Level1: limitSetting["0"],
Level2: limitSetting["1"],
Level3: limitSetting["2"],
}
// script
luaScript := `
local point = tonumber(ARGV[1])
local limit = cjson.decode(ARGV[2])
local expired = tonumber(ARGV[3])
-- 先GET一次KEY, 没有KEY的要SET, SET 同时要EXPIRE
if( redis.call('GET', KEYS[1]) == nil or redis.call('GET', KEYS[1]) == false) then
redis.call('SETEX', KEYS[1], expired, 0)
end
if( redis.call('GET', KEYS[2]) == nil or redis.call('GET', KEYS[2]) == false) then
redis.call('SETEX', KEYS[2], expired, 0)
end
if( redis.call('GET', KEYS[3]) == nil or redis.call('GET', KEYS[3]) == false) then
redis.call('SETEX', KEYS[3], expired, 0)
end
-- level 1
if(redis.call('GET', KEYS[1]) + point <= tonumber(limit.Level1)) then
redis.call('INCRBY',KEYS[1], point)
else
-- 定义不同的回传值来区分踩到的限额是哪一个
return tostring(-99)
end
-- level 2
if(redis.call('GET', KEYS[2]) + point <= tonumber(limit.Level2)) then
redis.call('INCRBY',KEYS[2], point)
else
-- 定义不同的回传值来区分踩到的限额是哪一个
return tostring(-98)
end
-- level 3
if(redis.call('GET', KEYS[3]) + point <= tonumber(limit.Level3)) then
redis.call('INCRBY',KEYS[3], point)
else
-- 定义不同的回传值来区分踩到的限额是哪一个
return tostring(-97)
end
return 'ok'
`
script, err := conn.ScriptLoad(luaScript).Result()
if err != nil {
return err
}
reply, err := conn.EvalSha(script, keys, point, tmp.MarshalBinary(), expired).Result()
if err != nil {
return err
}
fmt.Println("reply:", reply)
// TODO: 处理 reply 回传值对应资讯
return
}
// lua-script json decode 使用
func (s *LimitSetting) MarshalBinary() (ret string) {
data, _ := json.Marshal(s)
ret = string(data)
return ret
}
// lua-script json decode 使用
func (s *LimitSetting) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, s)
}
rpc.go 改打新的 function PointSetBatch
err = coconut_redis.PointSetBatch(s.RedisClient, keys, int(in.Point), limitSettings, 30)
if err != nil {
Logger.WithFields(map[string]interface{}{
"test": 111,
"time": time.Now().UnixNano(),
"err:": err.Error(),
}).Errorf("redis.PointSetBatch")
return nil, coconutError.ParseError(coconutError.ErrRedis, err)
}
测试v1, v2 版本差异
在不同的port启动两个不同分支的服务&不同台redis, 同时进行测试。
func main() {
// 连线到远端 gRPC 服务器。
conn1, err := grpc.Dial("localhost:3100", grpc.WithInsecure())
if err != nil {
log.Fatalf("conn 连线失败:%v", err)
}
defer conn1.Close()
coco1 := coconut.NewCoconutClient(conn1)
// 连线到远端 gRPC 服务器。
conn2, err := grpc.Dial("localhost:3200", grpc.WithInsecure())
if err != nil {
log.Fatalf("conn 连线失败:%v", err)
}
defer conn2.Close()
coco2 := coconut.NewCoconutClient(conn2)
n, _ := strconv.Atoi(os.Args[1])
var (
sum1 float64
sum2 float64
)
req := &coconut.PointsRequest{
Level_1: "aaa",
Level_2: "bbb",
Level_3: "ccc",
Point: 100,
}
wait := &sync.WaitGroup{}
for i := 0; i < n; i++ {
wait.Add(1)
go func() {
defer func() {
wait.Done()
}()
start := time.Now()
_, _ = coco1.UpdatePoints(context.Background(), req)
sum1 += time.Since(start).Seconds()
}()
}
for i := 0; i < n; i++ {
wait.Add(1)
go func() {
defer func() {
wait.Done()
}()
start := time.Now()
_, _ = coco2.UpdatePoints(context.Background(), req)
sum2 += time.Since(start).Seconds()
}()
}
wait.Wait()
fmt.Println("[v1] total count:", n, ", avg execute_time:", (sum1 / float64(n)))
fmt.Println("[v2] total count:", n, ", avg execute_time:", (sum2 / float64(n)))
}
测试 10次, 100次, 1000次的执行结果
经过多次分别测试10,100,1000的结果可以观察到, 10次的时候两个版本处理时间差异不大, 100~1000次就可以看出明显的差异, 这样就可以把版本调整为v2版了。
前言 初次参加铁人赛,开赛第一天,先放轻松暖个身,把前辈的文章看过一遍吧! 相关文章 Mike Fa...
立案流程 第一步: 到公司网查询是否有同名的公司,输入 1-5 个想要设立的公司名称,确认公司名称是...
3-6 阿伯日後谈 博览会後,太阳渐渐下山了,看着人渐渐散,阿伯也准备收拾摊子回家了,远处看见飞哥跟...
上一篇的 repository 还欠一个 mapper 把 EtaResponse 转成 EtaRe...
建立资料仓库是一个解决企业资料问题应用的过程,是企业资讯化发展到一定阶段必不可少的一步,也是发展资料...