利用分布式缓存实现分布式锁

背景

之前在系统中有做一个在客户端重试时,进行去重的逻辑,大致思路是将客户端的请求缓存到分布式缓存中,每次请求到达时先在缓存中查询,如果有就直接返回,没有就帮用户重试该请求。一天晚上(不凑巧,刚好是结婚当天),一个微信会话来了,某某老大的评论重复……

分析

上面在写数据前,会先请求一次缓存,但是这个操作在分布式环境下显然会有如下场景出现:

① 节点AGet数据,返回不存在

② 节点BGet数据,返回不存在

③ 节点A写数据

④ 节点A返回

⑤ 节点B写数据

⑥ 节点B返回

 

根本原因就是Get数据和Set数据不是原子操作,导致出现了AB都认为数据不存在。

问题很明显,就是没有对数据操作进行同步,如果在单机环境下,这个可以很简单的通过操作系统提供的各种同步原语处理,但是现在节点AB是处在不同机器上,这就涉及到分布式锁的问题了。类似于单机环境下的线程同步原语,我们只需要一种机制让应用程序知道某个资源被占用了(例如mutex如果lock失败,操作系统即会将该进程挂起),在分布式缓存中一般都存在一个叫做add的操作,该操作保证只有在资源不存在时才能执行成功,否则会告知调用者失败,且标注为特殊的错误码。

 

实现

这里只简单给出了获取锁一般实现伪代码,不同的业务场景有不同的处理,比如失败后继续重试直到成功。

memcache中:

if(cache->add(key, value, expire))
{
//get lock successful
}
else
{
//get lock fail
}

Redis中可以通过SET命令(2.6.12以上版本)或者SETNX命令,类似memcache中的ADD

SET key value NX EX max_lock_time。在redis-py中有一个封装好的lock实现可以直接使用。

总结

memcacheredis这里系统一般都是作为缓存来使用,但是在某些时候通过深入挖掘其实也可以有一些意想不到的作用,通过一个简单的语句既可以实现一个基本上够用的分布式锁,其性价比不言而喻。在网上随便一搜,也有好多类似的同行遇到这个问题,下面是几个链接,有国产的也有国外:

http://abhinavsingh.com/blog/2009/12/how-to-use-locks-for-assuring-atomic-operation-in-memcached/

http://chuyinfeng.com/p/39

http://blog.webfuns.net/archives/1722.html

http://jiangbo.me/blog/2011/02/27/post/