Redis优化了ZRANGEBYSCORE所用的时间

Viv*_*oel 3 lua redis

我正在使用redis排序集的队列系统.我的lua脚本看起来像:

local moveElement = function(source, dest , score, destscore)
    local element = redis.pcall('ZRANGEBYSCORE', source, '-inf',score, 'WITHSCORES' , 'LIMIT' , '0' , '1')
    if element ~= false and #element ~= 0 then
        redis.call('ZADD' , dest , destscore , element[1])
        redis.call('ZREM' , source , element[1])
    end
end
local temp = moveElement(KEYS[2], KEYS[1] , ARGV[2])
local temp = moveElement(KEYS[3], KEYS[1] , ARGV[2])
local score= redis.call('ZRANGEBYSCORE', KEYS[1], '-inf',ARGV[1], 'WITHSCORES' , 'LIMIT' , '0' , '10')
if score ~= false and #score ~= 0 then
    local i = 1
    while i<=#score do
        redis.call('ZREM', KEYS[1] , score[i])
        redis.call('ZREM', KEYS[2] , score[i])
        redis.call('ZADD', KEYS[3], ARGV[1] , score[i])
        i=i+2
    end
end
return score
Run Code Online (Sandbox Code Playgroud)

这个lua脚本在排序集中有6个成员需要24秒.

SLOWLOG GET 10

1) 1) (integer) 5937
   2) (integer) 1385993558
   3) (integer) 24446
   4) 1) "EVAL"
      2) "local moveElement = function(source, dest , score, destscore)                 local element = redis.pcall('ZRANGEBYSCORE', sourc... (937 more bytes)"
Run Code Online (Sandbox Code Playgroud)

我的代码方法是:

我用以下参数调用此脚本.

    1. [键数]
  1. foo1排序集名称有6k.
  2. foo2排序集名称.
  3. foo3排序集.移动foo1到foo2的成员.
  4. 当前时间戳.
  5. 当前时间戳 - 6分钟.

有没有办法优化时间?通过缓存lau脚本或其他东西?

因为我总是使用ZRANGE帮助取代ZRANGEBYSCORE从低到高取值?

更多细节:

我正在尝试实现某种类型的队列系统.逻辑在这里:

  1. foo1包含具有得分作为时间戳的成员,在该时间戳需要被触发.
  2. 每当事件需要将来解雇时.作为日程时间,将得分作为foo1.
  3. 我的脚本读取所有成员(逐个),得分小于或等于foo1的当前时间戳,并将它们移动到foo3.这是逐个完成的,元素被移动.此元素已分配给某个工作人员.(隐)
  4. 如果工人完成工作.它从foo3中删除成员.
  5. 超过x秒超时如果成员没有删除foo3.脚本将其移回foo1.因此可以将其重新分配给其他工作人员.

cat*_*ell 5

您发布的脚本存在一些问题.这是在Lua(请不要发布引用的字符串,它使代码难以阅读):

local moveElement = function(source, dest , score, destscore)
  local element = redis.pcall(
    'ZRANGEBYSCORE', source, '-inf', score, 'WITHSCORES' , 'LIMIT' , '0' , '1'
  )
  if element ~= false and #element ~= 0 then
    redis.call('ZADD' , dest , destscore , element[1] )
    redis.call('ZREM' , source , element[1])
  end
end

local temp = moveElement(KEYS[2], KEYS[1] , ARGV[2])
local temp = moveElement(KEYS[3], KEYS[1] , ARGV[2]) 
local score= redis.call(
  'ZRANGEBYSCORE', KEYS[1], '-inf', ARGV[1], 'WITHSCORES' , 'LIMIT' , '0' , '10'
)
if score ~= false and #score ~= 0 then
  local i = 1
  while i<=#score do
    redis.call('ZREM', KEYS[1] , score[i])
    redis.call('ZREM', KEYS[2] , score[i])
    redis.call('ZADD', KEYS[3], ARGV[1] , score[i])
    i=i+2
  end
end
return score
Run Code Online (Sandbox Code Playgroud)

首先,第5行,元素永远不会false.我怀疑你正在尝试捕获错误,但在这种情况下,它将是一个带有err字段的表.同样的事情score进一步向下.

然后,您分配两个局部temp变量,以后再也不使用它们.

你的while循环更好用for:

for i=1,#score,2 do
  redis.call('ZREM', KEYS[1] , score[i])
  redis.call('ZREM', KEYS[2] , score[i])
  redis.call('ZADD', KEYS[3], ARGV[1] , score[i])
end
Run Code Online (Sandbox Code Playgroud)

你为什么WITHSCORES在moveElement中使用?你不使用它.

除此之外,你能更清楚地解释一下你想要达到的目标,这样我才能进一步帮助你吗?这样的操作无论如何都不应该花费24秒(除非你在古董机器上运行).

编辑

这是一个更简单的脚本版本,考虑到你到目前为止告诉我的内容:

local now = ARGV[1]
local timed_out = ARGV[2]
local pending_jobs = KEYS[1]
local running_jobs = KEYS[2]
local jobs

local not_empty = function(x)
  return (type(x) == "table") and (not x.err) and (#x ~= 0)
end

-- check if some jobs have timed out
-- if yes, take the oldest one and queue it again
jobs = redis.pcall(
  'ZRANGEBYSCORE', timed_out, '-inf', timed_out, 'LIMIT', '0', '1'
)
if not_empty(jobs) then
  redis.call('ZADD', pending_jobs, now, jobs[1])
  redis.call('ZREM', running_jobs, jobs[1])
end

-- check if there are jobs ready
-- if yes, take the 10 oldest ones and run them
jobs = redis.pcall(
  'ZRANGEBYSCORE', KEYS[1], '-inf', ARGV[1], 'LIMIT', '0', '10'
)
if not_empty(jobs) then
  for i=1,#jobs do
    redis.call('ZREM', pending_jobs, jobs[i])
    redis.call('ZADD', running_jobs, now, jobs[i])
  end
end

return jobs
Run Code Online (Sandbox Code Playgroud)

看看这个脚本,我完全没有理由认为它需要多达24秒.所以要么:

  • 您的Redis设置有问题,或者
  • 你真的不是这样做的,或者
  • 你的测量是错误的.

除非你能提供一个展示这个问题的数据,否则我可以说更多...