目录
1 使用背景
随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题。
分布式锁主流的实现方案:
1. 基于数据库实现分布式锁
2. 基于缓存(Redis等)
3. 基于Zookeeper
每一种分布式锁解决方案都有各自的优缺点:
1. 性能:redis最高
2. 可靠性:zookeeper最高
这里,我们就基于redis实现分布式锁。
2 使用redis实现分布式锁
1.访问资源的时候设置一个互斥锁:
set key value nx ex time
在设置值的同时,使用nx加锁,表示只有当该键不存在的时候才能对键进行设置
ex time 表示给键设置过期时间,用来防止键被一个请求获得,但是又因为种种原因不能释放锁,导致其他线程不能获取锁,从而无法对资源访问。
需要释放锁的时候使用 del key将该key删除掉,就可以释放这个互斥锁。
2.上述实现锁的方式是有问题的:
可能会释放其他服务器的锁。(获得锁后,由于服务器卡顿,导致没有在key到期时间内释放锁,key就会由于到期而释放锁。但是该服务器没有死掉,后面在其他服务器获得锁之后,该服务器执行后面操作,将key的锁给释放掉了)
解决方法:每个线程设置一个uuid,存入锁对应的值。之后在释放锁的del操作处,先判断当前uuid是不是自己的uuid,有没有每其他线程改过,如果改过就说明锁已经被释放且由别的线程获得。就不能删除锁对应的key。
3.但其实,这个方法也是有问题的:
可能会释放其他服务器的锁。(获得锁后,由于服务器卡顿,导致没有在key到期时间内释放锁,key就会由于到期而释放锁。但是该服务器没有死掉,后面在其他服务器获得锁之后,该服务器执行后面操作,将key的锁给释放掉了)
解决方案:使用LUA脚本来保证原子性。