8

在更新和获取键/值时,我正在使用 ServiceStack Redis 中的 AcquireLock 方法,如下所示:

public virtual void Set(string key, T entity)
{
    using (var client = ClientManager.GetClient())
    {
        using (client.AcquireLock(key + ":locked", DefaultLockingTimeout, DefaultLockExpire))
        {
            client.Set(key, entity);
        }
    }
}

我已经扩展了 AcqurieLock 方法来接受额外的参数来锁定密钥的到期。所以我想知道我是否需要AcquireLock?我的班级在 Get<>、GetAll<>、ExpireAt、SetAll<> 等每个操作中都使用 AcquireLock。

但这种方法并非每次都有效。例如,如果锁中的操作抛出异常,则密钥保持锁定状态。对于这种情况,我在 AcquireLock 方法中添加了 DefaultLockExpire 参数以使“锁定”密钥过期。

有没有更好的解决方案,或者我们什么时候需要在多线程编程中获取像“锁”块这样的锁。

4

3 回答 3

11

正如 The Real Bill 的回答所说,Redis 本身不需要锁。ServiceStack 客户端在锁定方面提供的不是 Redis,而是您的应用程序。在 C# 应用程序中,您可以在本地锁定事物,lock(obj)这样就不会同时发生某些事情(一次只有一个线程可以访问锁定的部分),但这只有在您有一个网络服务器时才有效。如果您想防止某些事情同时发生,您需要一个位于网络服务器之外的锁定机制。Redis 非常适合这种情况。

我们有一个案例,检查客户是否已经有购物车,如果没有,则创建它。在检查和创建它之间,有一段时间另一个请求也可能发现购物车不存在并且也可能继续创建一个。这是锁定的经典案例,但简单的情况lock在这里不起作用,因为请求可能来自完全不同的 Web 服务器。因此,为此,我们使用ServiceStack Redis 客户端(带有一些抽象)来使用 Redis 进行锁定,并且一次只允许一个请求进入“创建购物车”部分。

因此,要回答您的实际问题:不,您不需要锁定来获取/设置 Redis 的值。

于 2013-03-02T22:40:00.950 回答
3

我不会将锁用于获取/设置操作。Redis 会以原子方式执行这些操作,因此在设置或获取时不会“在你下面改变”。我已经构建了数百个客户端同时更新/操作值的系统,并且从不需要锁来执行这些操作(尤其是过期)。

我不知道 Service Stack redis 如何实现它的锁定,所以我不能说它为什么会失败。但是,我不确定我是否会相信它,因为 Redis 端不需要真正的锁定来进行数据操作。Redis 是单线程的,因此在那里锁定没有意义。

如果您正在执行获取值的复杂操作,请基于它对事物进行操作,然后在一段时间后对其进行更新并且同时不能更改值我建议您阅读和摸索http://redis.io /topics/transactions看看你想要的是否是 Redis 的优点,你的代码是否需要重构以消除问题,或者至少找到更好的方法来做到这一点。

例如,SETNX可能是您获得想要的东西所需的路线,但如果没有详细信息,我不能说它会起作用。

于 2013-03-02T19:45:38.183 回答
1

正如@JulianR 所说,ServiceStack.Redis 中的锁定仅适用于应用程序级分布式锁(即使用数据库或.lock分布式文件系统上的空文件进行替换),它仅适用于其他进程中的其他 ServiceStack.Redis 客户端使用相同的密钥/API 来获取锁。

对于正常的 Redis 操作,您永远不需要这样做,因为它们都是原子的。如果您想确保 redis 操作的组合以原子方式发生,而不是在 Redis Transaction 中组合它们,或者您可以在服务器端 Lua 脚本中执行它们- 两者都允许原子执行批处理操作。

于 2013-03-03T01:46:21.087 回答