[锁机制] 分布式锁,redis和zookeeper的实现
1.分布式锁
现在的系统都是微服务模型,即各种组件单独运行在逻辑独立的机器中,之间通过rpc等方式调用并完成服务。
也因此数据的保存和数据的计算往往不在同一个地方,需要一个分布式框架的“全局锁”。其余所有系统都需要通过它获取锁。
实现分布式锁功能的,可以是redis这样的缓存数据库,也可以是MySQL这样的关系数据库,也可以是Zookeeper这样的分布式协调服务。
2.redis实现分布式锁
思路是在redis中专门使用一个string类型变量保存对应资源是否锁定。
1)访问资源前,通过setNX(set not exist)尝试获取锁,如果key为空则成功返回1,否则返回0。
命令类似于:
SET Lock TimeStamp NX
其中 Lock 为对应资源的key, TimeStamp为当前时间戳。
2)若setNX返回0,说明获取锁失败。考虑到可能有其他服务获取锁导致死锁的情况,可以获取Lock对应的TimeStamp值,检查时间戳是否过期(即资源已被占用超过一定时间)。如果超过时间则进行解锁,更新时间戳,否则需要等待锁释放。
3)释放锁
删除redis中的Lock值就行了。即 DELETE命令。
4)另外一种情况:不保存timstamp值,而是使用PX millionseconds参数来指定有效时间,到时会自动过期。
3.zookeeper实现分布式锁
zookeeper中基本的操作时创建和删除节点,节点为树状结构。
有序节点:创建后zookeeper会自动添加节点号,例如创建 lock-resource- ,那么zookeeper可能创建lock-resource-0000000001。
使用zookeeper的临时有序节点。获取锁即在zookeeper创建一个临时有序的节点。为了便于管理,将节点创建在保存Lock的父节点下。
临时节点:客户端会话结束或者超时后,zookeeper会自动删除该节点。
创建成功后,需要判断当前线程持有的节点是否是序号最小的节点。若是序号最小,则获取锁成功。
否则对当前节点序号的前一个节点添加一个事件监听,如果节点删除(锁释放了),则自己是最小的节点,那么获取到锁。
4.比较
redis:
1)需要反复尝试获取锁,消耗性能。
2)redis可能会有健壮性问题。
zookeeper:
1)天然支持分布式,健壮性强。
2)不需要反复尝试获取锁,可以添加监听器。