golang redigo lua解决性能问题

前言:

    我们知道使用redis计数可以使用incrby, hincrby 等计数指令,因为redis的工作线程只有一个,所以保证了并发原子的控制。  由于我们的业务的特殊性,有增有减,有各类状态值的判断,尤其在异常情况下,计数会减到0以下,负数是不能忍的。 所以,我们需要尽量保证各个计数器值是正整数。 


该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新。  http://xiaorui.cc/?p=4737

我先前的做法是这样, 先判断各种业务在redis中的各种标志位,然后尝试去操作计数,如果拿到的计数值小于0,那么我们给他摆正。 逻辑还是稍显复杂,来回的状态判断就有十几回.

redis command

先前的代码片段如下 (实际逻辑代码过多,就简单举个例子说明):

func CounterDecrByAppid(app_id string, process string) error {
	rc := RedisClient.Get()
	defer rc.Close()

	app_id = mergeCounterKey(app_id)
	c, err := redis.Int(rc.Do("HINCRBY", app_id, process, -1))
	if c < 0 {
		fmt.Println("ccc", c)
		rc.Do("HINCRBY", app_id, process, 1)
	}

	return err
}

上面的代码没问题,可以跑… 但是首先不够原子,比如并发都拿到了 -1 值,接着都去 set 1, 这样显而损失了不少精度.  另外一个大点就是性能…  


pipeline批量 ? 是可以解决…  但我们知道pipeline可以减少网络来回的RTT,你也可以在一个管道中混入读写,但问题是,你还是需要做判断… 你的判断比如又要来回…


后面,我们改用redis lua来实现这一块逻辑.   下面的例子是代码片段,其实线上的逻辑要比这复杂的多。 简单说,如果每次判断值都要io来往,那么时间的消耗是必然的。 所以,我们可以把相关的逻辑注册到redis eval方法里,这样只需一次io访问就完成了该逻辑链。  

如果没有使用redis lua的朋友,可以看看我以前写过的redis lua eval的文章。  http://xiaorui.cc/?p=3052

性能测试

性能方面,针对这一块逻辑测试,redis lua要比单条redis性能高很好。如果redis lua方法只是单条指令,那么他的速度要比redis慢…. 原因你懂得…  

// xiaorui.cc

const (
	// key, field, num
	SCRIPT_INCR = `
local v = redis.call("HINCRBY", KEYS[1], ARGV[1], ARGV[2])
if v < 0
then
	local c = tonumber(ARGV[2])
	if c < 0
	then
		return redis.call("HSET", KEYS[1], ARGV[1], 0) and 1
	else
		return redis.call("HSET", KEYS[1], ARGV[1], 1) and 1
	end
else
	return 0
end
`
)

func CounterRegScript(app_id string, status string) (int, error) {
	rc := RedisClient.Get()
	defer rc.Close()

	script := redis.NewScript(1, SCRIPT_INCR)
	resp, err := redis.Int(script.Do(rc, app_id, status, int64(-1)))
	return resp, err
}

中间遇到一些问题,mac上的redis-server是3.2,测试主机2.4 … 对的,相当古老的 2.4 .   也不知道哪位大哥这么仇恨我,居然给我转了个2.4… 导致我忙活了好长时间…   因为先前确认这逻辑没问题,就没有在判断 err 的状态… 

你虽然传入的是int, 但是 redis的lua解释器不认,所以需要用toNumber去转换到 数字对象.  不然会出现下面的问题.  

ERR Error running script (call to f_7778767932161a6c2339144e0c2daefa12c51609): @user_script:7: user_script:7: attempt to compare string with number

redis注册lua脚本的时候,会根据内容hash一个code,通过这个code是可以映射相关的lua自定义函数.

502873561.856256 [0 127.0.0.1:56216] "SELECT" "0"
1502873561.856356 [0 127.0.0.1:56216] "EVALSHA" "a1ca4f6db2d614463cb4fef83fd9894168423bb5" "1" "app_id_99" "RUNNING" "-1"
1502873561.856390 [0 lua] "HINCRBY" "app_id_99" "RUNNING" "-1"
1502873561.856417 [0 lua] "HSET" "app_id_99" "RUNNING" "0"

END.


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc