从AppFabric切换到Redis时处理分布式锁

时间:2017-06-30 17:14:47

标签: .net redis appfabric stackexchange.redis

我们正在将一个相当大的高流量网站从AppFabric迁移到Redis。我们正在使用.NET和StackExchange.Redis库。我有点难以理解的一个领域是分布式锁,我们非常依赖它。在AppFabric中,典型的模式是:

  1. 调用GetAndLock,它将锁定一个键并返回一个锁定句柄。
  2. 使用C#代码
  3. 更新密钥中的一些内容
  4. 调用PutAndUnlock,传入相同的锁定句柄
  5. 如果另一个线程/进程/主机尝试更新相同的数据,则其GetAndLock调用将失败,并且它有一些逻辑等待几毫秒并重试x次。

    我试图确定使用Redis的最佳方法,Redis本身并不支持分布式锁。

    在大多数情况下,我们需要的是一种更新缓存中序列化POCO对象的单个属性的方法。例如,如果我们的数据结构中包含在缓存中序列化的用户枚举,并且我们想要更新这些用户的一个的单个属性,我们就会锁定密钥,更新该用户的属性,将其序列化,并解锁密钥。如果另一台计算机正在更新另一个用户,这将阻止对另一个用户的更新丢失。

    所以,我对这个主题进行了一些研究,并且有一些看似有希望的解决方案,或者至少可以了解更多。

    StackExchange.Redis锁定

    这条道路似乎是死路一条。 LockTakeLockRelease似乎没有锁定特定资源,它们似乎只是用于实现某种锁定系统的构建块。我无法找到足够的文档来确定它是否在我的场景中有用。

    RedLock.net

    我找到了RedLock.net库,它建立在StackExchange.Redis上。它似乎实现了一个分布式锁定系统,有点类似于AppFabric的工作方式。您可以获取对资源的锁定,获取某种句柄,更新内容,然后解锁该资源。

    然而,使用这个意味着引入一个新的库并且设置起来似乎相当复杂(设置端点和配置事物的方式完全不同)。阅读它听起来也可能很慢;只有当锁可以分配给至少一半的机器时才获得锁。我也发现很多人都说,除非你做的事情非常复杂,否则你不应该这样做。

    交易

    StackExchange.Redis documentation on Transactions似乎很有趣。众所周知,您可以使用事务将Redis命令作为原子单位执行,并且它们将排队并立即执行。但是,更新对象的序列化JSON属性不是Redis命令,并且当该事务执行时,我无法回调到C#代码。所以,这似乎不起作用。但是等等,他们有这个概念涉及观看一把钥匙。如果它发生变化,我们将中止我们的交易并回滚。我想知道我是否可以利用该机制,只需在密钥更改时回滚,然后重试。这看起来可能对键有点过于乐观了很多事情正在争夺并且会导致很多重试,而不是等待"等待"获得锁定。

    手动跟踪缓存值的版本

    AppFabric的另一个好处是每个缓存值的CacheVersion属性。您可以查看缓存中某些内容的值,并查看其版本。如果版本自您阅读后发生了变化,您就会知道它很脏并重新加载它。我们使用了这个,我正在考虑在Redis中实现我自己的序列化,它将序列化一个独特的版本属性以及缓存中的所有内容。如果我这样做,我可能会在事务中添加某种约束,如果版本与我最初加载的版本不匹配则中止。这可能会遇到与前一个想法相同的许多问题。

    还有其他人找到了解决这类问题的好方法吗?显然,最好的解决方案是将这些较大的对象分解成一堆微小的,单独的键,我们可以原子地更新,但不幸的是,这一点在目前是完全不可行的。

0 个答案:

没有答案