当前位置: 首页 > 面试题库 >

Ruby-具有到期实现的基于Redis的互斥锁

邢项禹
2023-03-14
问题内容

我正在尝试使用Redis实现基于内存的多进程共享互斥体,该互斥体支持超时。

我需要互斥锁是非阻塞的,这意味着我只需要能够知道是否能够获取互斥锁,如果不能,则只需继续执行后备代码即可。

遵循以下原则:

if lock('my_lock_key', timeout: 1.minute)
  # Do some job
else
  # exit
end

一个 未到期的互斥 可以使用Redis的的实现setnx mutex 1

if redis.setnx('#{mutex}', '1')
  # Do some job
  redis.delete('#{mutex}')
else
  # exit
end

但是,如果我需要具有超时机制的互斥锁(为了避免在redis.delete命令之前红宝石代码失败,导致互斥锁被永久锁定的情况,例如,但并非仅出于此原因)。

做这样的事情显然是行不通的:

redis.multi do  
  redis.setnx('#{mutex}', '1')
  redis.expire('#{mutex}', key_timeout)
end

因为即使我无法设置互斥锁,我也会重新设置该互斥锁的过期时间(setnx返回0)。

自然,我本来希望拥有类似的功能setnxex,即自动使用到期时间设置键的值,但前提是该键尚不存在。不幸的是,据我所知,Redis不支持此功能。

但是,我确实找到了find renamenx key otherkey,这使您可以将一个键重命名为另一个键,前提是另一个键不存在。

我想到了这样的内容(出于演示目的,我将其完整地写下来,并且没有将其分解为方法):

result = redis.multi do
  dummy_key = "mutex:dummy:#{Time.now.to_f}#{key}"
  redis.setex dummy_key, key_timeout, 0
  redis.renamenx dummy_key, key
end
if result.length > 1 && result.second == 1
  # do some job
  redis.delete key
else
  # exit
end

在这里,我为虚拟密钥设置了到期时间,并尝试将其重命名为真实密钥(在一次交易中)。

如果renamenx操作失败,那么我们将无法获取互斥体,但不会造成任何危害:虚拟密钥将过期(可以选择添加一行代码立即将其删除),并且真实密钥的到期时间将保持不变。

如果renamenx操作成功,则我们可以获得互斥量,并且互斥量将获得所需的到期时间。

任何人都可以看到上述解决方案的任何缺陷吗?是否有针对此问题的更标准解决方案?我真的很讨厌使用外部gem来解决这个问题。


问题答案:

如果您使用的是Redis
2.6+,则可以使用Lua脚本引擎更轻松地完成此操作。在Redis的文件说:

Redis脚本在定义上是事务性的,因此您可以使用Redis事务进行任何操作,还可以使用脚本进行操作,通常该脚本会更简单,更快速。

实现它很简单:

LUA_ACQUIRE = "return redis.call('setnx', KEYS[1], 1) == 1 and redis.call('expire', KEYS[1], KEYS[2]) and 1 or 0"
def lock(key, timeout = 3600)
  if redis.eval(LUA_ACQUIRE, key, timeout) == 1
    begin
      yield
    ensure
      r.del key
    end
  end
end

用法:

lock("somejob") { do_exclusive_job }


 类似资料:
  • 我试图使用ServiceStack-Redis库提供的锁定机制实现DLM,并在此进行了描述,但我发现API似乎呈现了一种竞争条件,有时会将相同的锁授予多个客户端。 当运行上述代码来模拟两个客户端在短时间内相继尝试相同的操作时,有三个可能的输出。第一种是互斥体正常工作、客户端按正确顺序进行的最佳情况。第二种情况是当第二个客户端在等待获取锁时超时;也是一个可以接受的结果。然而,问题是,当接近或超过获取

  • 问题内容: 我想在我的node.js应用程序内实现互斥锁,这是Wiki http://en.wikipedia.org/wiki/Mutual_exclusion中的互斥锁。 这个主题有没有准备好的模块?如果没有,有什么想法可以帮助我实施它吗? 问题答案: 有很多方法可以完成此任务。有两种简单的方法是通过Redis或Zookeeper服务器。Node.js两者都有很好的模块。 在Redis中,您可

  • 我需要构建一个具有优先级的“不公平信号量”。例如:当优先级为1的线程想要获取信号量时,它只需等待具有相同优先级的另一个线程完成,然后就可以获取()。但是,当优先级为2的线程想要获取信号量时,它必须等待优先级为1的所有线程完成后才能使用信号量,然后尝试获取()。我总共有4个不同的优先事项。这是我尝试过的,但没有成功。 有人有什么解决办法吗?

  • 问题内容: 阅读有关锁定PHP的一些文章。 它们主要都直接指向http://php.net/manual/en/function.flock.php。 本页讨论如何在硬盘上打开文件! 真的是这样吗?我的意思是,这使锁定变得非常昂贵-这意味着每次要锁定时,我都必须访问硬盘)= 能再给我一个令人愉快的消息安慰我吗? 编辑: 由于我已经收到了一些答复,我想问这个。 我的脚本只能由一个或多个线程运行?因为

  • Go语言包中的 sync 包提供了两种锁类型:sync.Mutex 和 sync.RWMutex。 Mutex 是最简单的一种锁类型,同时也比较暴力,当一个 goroutine 获得了 Mutex 后,其他 goroutine 就只能乖乖等到这个 goroutine 释放该 Mutex。 RWMutex 相对友好些,是经典的单写多读模型。在读锁占用的情况下,会阻止写,但不阻止读,也就是多个 gor

  • 互斥是多线程系统中用于控制访问的一个原对象(primitive object)。下面的例子给出了它最基本的用法: std::mutex m; int sh; //共享数据 // … m.lock(); // 对共享数据进行操作: sh += 1; m.unlock(); 在任何时刻,最多只能有一个线程执行到lock()和unlock()之间的区域(通常称为临界区)。当第一个线程正在临界区执行时