[zeromicro/go-zero]一旦到达限流的次数,redis不再执行incrby

2024-03-05 539 views
4

一旦到达限流的次数,redis不再执行incrby

回答

3

pr中代码冗余redis.call("INCRBY", KEYS[1], 1)是为了减少条件判断的嵌套和增加可读性。

另附减少冗余代码redis.call("INCRBY", KEYS[1], 1)的版本:

local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = tonumber(redis.call("get", KEYS[1]) or "0")
if current +1 <= limit then
    redis.call("INCRBY", KEYS[1], 1)
    if current == 0  then
        redis.call("expire", KEYS[1], window)
        return 1
    elseif current+1 < limit then
        return 1
    else
        return 2
    end
else
    return 0
end
3

PR里代码跟我原来代码作用完全相同呀

并且过了limit加和不加效果一样的

7

作用相同,但过了limit加和不加效果是不一样的,这会影响QPS。代码lua脚本总是执行incrby进行递增,incrby属于写操作;而PR中lua脚本在超过了limit之后,就不再执行incrby了,仅执行的是get,get属于读操作。这样就减少了写redis的写操作,提升了性能。

另附:两者benchmark测试

redis get vs incr redis get的qps redis get redis incr的qps 屏redis incr的qps

可见,一般情况下 ,读命令get 的qps高于 写命令incr

代码中lua vs 本次pr中的lua

加载脚本

  • 1.lua 为代码中lua脚本
  • 2.lua 为本次PR中lua脚本
    加载脚本

注:下面截图中 key_old 为代码中的key,key_new 为本次pr代码中的key

每秒最大允许100请求对比

每秒最大允许100请求对比

每秒最大允许1000请求 对比

每秒最大允许1000请求对比

每秒最大允许10000请求 对比

 每秒最大允许10000请求 对比

综上:减少了redis写操作,提升了qps。 注:在benchmark测试中,出现过极少量的写操作qps高于读操作的qps情况,属于测试的正常误差范围。

两脚本

1.lua (代码中lua脚本)

local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = redis.call("INCRBY", KEYS[1], 1)
if current == 1 then
    redis.call("expire", KEYS[1], window)
    return 1
elseif current < limit then
    return 1
elseif current == limit then
    return 2
else
    return 0
end

2.lua (本次PR中lua脚本)

local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = tonumber(redis.call("get", KEYS[1]) or "0")

if current == 0 then
    redis.call("INCRBY", KEYS[1], 1)
    redis.call("expire", KEYS[1], window)
    return 1
elseif  current+1 < limit then
    redis.call("INCRBY", KEYS[1], 1)
    return 1
elseif current +1 == limit then
    redis.call("INCRBY", KEYS[1], 1)
    return 2
else
    return 0
end
0

非常感谢你的详细的测试,不过我仔细看了数据,我的观点如下:

  1. 两者差别很小,但原有代码更简单直观
  2. 测试时大部分是超过阈值后的请求,对于常规状态下反而更慢了,因为先要get,再去incr
3

这两种写法平分秋色,我也有些纠结。我的观点如下:

  1. 新脚本在性能上有提升,但原有代码更简单直观
  2. 限流就是为了解决请求数量大于甚至是明显大于阈值的场景。一般情况 常规状态数量 要小于 超过阈值之后的数量。虽然先要get,再去incr消耗了一些时间,但是后期节约大量的incr时间
  3. redis 的写操作改变数据库的状态,还会有 master-slaves 同步的消耗 、AOF持久化的消耗 等此类 “暗耗”。 我个人更倾向于PR的写法
4

性能差异不大的情况下,简单更重要哈

9

@tylitianrui 这个PR我先不合哈