更新Redis键/值时获取锁定

时间:2013-02-20 14:27:13

标签: locking timeout redis servicestack

我正在使用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<&gt ;, ExpireAt,SetAll<>等所有操作中使用AcquireLock。

但这种方法并不是每次都有效。例如,如果锁中的操作引发异常,则密钥保持锁定状态。对于这种情况,我已将DefaultLockExpire参数添加到AcquireLock方法以使“锁定”键到期。

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

3 个答案:

答案 0 :(得分:6)

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

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

所以回答你的实际问题:不,你不需要锁定来获取/设置Redis的值。

答案 1 :(得分:3)

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

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

如果你正在进行复杂的操作,你得到一个值,根据它进行操作,然后在一段时间后更新它,并且在此期间不能进行值更改我建议阅读和查看{{3}查看你想要的是Redis有什么用处,你的代码是否需要重构以消除问题,或者至少找到更好的方法来做到这一点。

例如,http://redis.io/topics/transactions可能是您获得所需内容的路线,但如果没有详细信息,我不能说它会起作用。

答案 2 :(得分:1)

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

对于普通的Redis操作,你永远不需要这样做,因为它们都是原子的。如果你想确保redis操作的组合以原子方式发生,而不是combine them within a Redis Transaction,或者你可以execute them within a server-side Lua script - 两者都允许批量操作的原子执行。