Redis优化了ZRANGEBYSCORE所用的时间

时间:2013-12-02 14:17:39

标签: 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

这个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)"

我的代码方法是:

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

    1. [键数]
  1. foo1排序集名称为6k。
  2. foo2排序集名称。
  3. foo3排序集。移动foo1到foo2的成员。
  4. 当前时间戳。
  5. 当前时间戳 - 6分钟
  6. 有没有办法优化时间?通过缓存lau脚本或其他东西?

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

    更多详情:

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

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

1 个答案:

答案 0 :(得分: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

首先,第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

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

除此之外,你能更清楚地解释一下你想要达到的目标,这样我才能进一步帮助你吗?这样的操作无论如何都不应该花费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

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

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

除非您能为.rdb提供展示此问题的数据,否则我可以说更多...