Redis 支持在 LUA 脚本中使用任意精度

我需要能够在 Redis 中执行以下操作的事务:

  • 仅当结果 > 0 时才减少 n 值
  • 否则不执行任何操作
  • 处理任意精度的十进制数(我需要它们以浮点格式)
  • 可以被其他进程访问

简单地放,它就是一个“余额”:如果我在此字段中拥有足够的钱,我就可以使用它,否则不能使用。有时,它必须减少许多余额。

为此,我制作了一个 LUA 脚本,它计算减量的结果,然后用此结果修改字段。我选择这个解决方案,因为:

我面临的问题:

  • 使用的库不适合:它只适用于整数,太大了每次发送(即使使用 evalsha,速度也很慢)
  • 在 Redis 中编写 Lua 脚本时如何包括第三方库 => 这样做后,我对 Redis 上使用附加模块感到非常困惑。但是,这已经过去了。现在情况怎样?
  • 我甚至不确定是否有更有效的方法来做到这一点?欢迎对代码本身提出任何建议
  • Redis 真的是满足我的需求的一种方式吗?

“值”输入具有以下格式:Array<{ key: string, field: string, value: string // this is actually a BigNumber, with a string format }>

this.redisClient.eval(`
    ${luaBigNumbers}

    local operations = cjson.decode(KEYS[1])
    local isStillValid = true
    local test

    for k, v in pairs(operations) do
      local temp = BigNum.new(redis.call('hget', v.key, v.field))
      local res = BigNum.mt.add(temp, BigNum.new(v.value))

      if BigNum.mt.lt(res, BigNum.new('0')) then
        isStillValid = false
      end
    end

    if isStillValid then
      for k, v in pairs(operations) do
        local temp = BigNum.new(redis.call('hget',v.key, v.field))
        redis.call('hset', v.key, v.field, BigNum.mt.tostring(BigNum.mt.add(temp, BigNum.new(v.value))))
      end
    end

    return tostring(isStillValid)`,
  1, JSON.stringify(values), (err, reply) => {

总之:我需要在 Redis 中具有共享余额功能,怎样做得好?

如果您知道如何实现,请发布到 Stack Exchange 中 https://softwareengineering.stackexchange.com/questions/391529/what-architecture-is-the-most-adapted-for-a-shared-balance-in-nodejs-and-maybe

点赞
用户2707363
用户2707363

也许受事件溯源模式的启发可以解决您的问题。另外,实现原子性的另一种方法是将写入角色限制为仅有一个处理器,其命令将始终按时间顺序排列(就像redis和lua一样)

1)您将带有余额更改事件的“事件”存储在排序集中(用于时间排序,时间戳作为得分)。仅存储您想要执行的“命令”(而不是计算结果)。例如,-1.545466,“+2.07896" 等等……

2)然后您通过一个Lua脚本从单个处理器(必须确保只有一个计算项访问此数据,否则会有麻烦)中使用这些事件进行消耗,可以使用循环调用脚本,每隔n秒调用一次(你可以定义你的实时质量)就像Apache Storm一样(一个“麦克风”)。例如,该脚本应从最旧的时间戳到最新的时间戳返回事件,应同时返回时间戳(得分),当然还要返回实际余额。

您应该得到像这样的值:

balance= +5
ZSET=
"-6" score 1557782182
"+2" score 1557782772
"+3" score 1678787878

3)在您的中间件服务器(唯一的允许修改余额的服务器)中,使用您想要在服务器中使用的任何库/技术(应该非常快)计算余额的更改。您只需遍历事件以每次计算余额即可。请注意,由于此方法,您将在redis中执行较少的突变。

您应该得到以下结果

old_balance=5
new_balance=10
ZSET=
"-6" score 1557782182
"+2" score 1557782772
"+3" score 1678787878

4)在您的服务器计算出新的余额值后,就该通过Lua脚本将结果和您用于redis的事件发送回去。使用Lua脚本的原因有:

-更新余额的值,因为只允许一个进程进行修改,所以您不应该遇到任何交易问题,它也应该始终按时间顺序排列 -修剪计算事件的排序集(步骤2使用的最旧和最新时间戳将用于此),以便在下一个lua调用中不再处理这些事件

5)受益。

请注意,在调用操作2之前,操作4必须完成,您可以在redis中设置一个旧的信号量(例如“忙”键),以防止这种情况发生(如果操作4未完成,则设置一个忙键,以便防止操作2运行,当步骤4完成时清除它,您也可以设置它的到期时间,以便如果出现问题,则清除将作为另一个迭代的超时启动)。

2019-05-13 21:22:02