1.什么是分布式锁
分布式锁是控制分布式系统之间访问共享资源的一种方式。
在分布式系统中,常常需要协调他们的动作,如果不同的系统/服务/应用程序访问了同一个或一组资源,
那么在访问这些资源的时候,需要使用互斥防止彼此产生干扰来保证数据一致性,那么就需要使用分布式锁。
下图是分布式的一种场景,但是没有体现临界资源的概念,本人比较懒,直接摘抄原图。
下面JVM1,JVM2,JVm3实际指向同一个临界资源(举例:数据库),三个进程需要更新同一份数据,那么需要控制并发问题
2.分布式锁的特性
互斥:一个临界资源同一时间只能被一个线程执行
可重入:已经获取锁的人(尚未释放),可多次重新获取
失效机制:锁需要有一定的有效期,避免因为获取到锁的服务进程奔溃导致锁无法释放
非阻塞:获取锁成功或者失败,都立马返回,不阻塞(在有些场景下,获取不到锁的人,就不需要干活了,所以不应该阻塞)
高可用、高性能的获取锁和释放锁
3.分布式锁的实现方案列举
4.基于数据库的实现
悲观锁:直接使用某个临界资源表,获取锁的时候调用select ... for update来获取锁,其它竞争服务获取不到锁会阻塞等待,效果不好
乐观锁:新建临界资源表,每条锁对应一个记录,每条记录有一个版本号,更新数据时判断版本号是否和自己预期一致
5.基于zookeeper的实现
基本思想:各服务进程在某个锁下创建临时、有序节点, 同时注册watch监听事件
节点名最小的服务获取到锁
使用完锁时删除节点,后面的节点因为注册了watch监听事件,所以可以收到通知,下一个节点获取到锁
如果使用锁的过程中,进程崩溃,由于使用的是临时节点,zookeeper感知到进程退出后,会删除临时节点,后面的节点会收到通知
6.基于redis的实现
使用setnx竞争设置一个key,拿到key的人表示获取到了锁(setnx就是没有则设置)(互斥)
获取锁的同时需要增加超时时间设置,避免自己奔溃后,锁无法释放(这两步需要是原子操作)(失效机制)
使用锁的过程中,需要进行锁的保活,因为redis中key的超时时间是固定的,但是执行时间不固定的,避免key过期,其它进程同时获取到锁
释放锁的时候,需要检查这个key对应的值是否是自己,避免因为自己的锁失效(下一个人已经获取到锁),导致后面连锁反应,全部人都获取到锁
7.Redlock(红锁)
redlock是集群模式的redis分布式锁,它基于集群中n个的redis节点进行加锁。
主要是因为redis集群的数据一致性实时性相对较差,在一个节点上写成功的数据,其它节点不一定能看到,所以集群模式下,不能单独的锁一台redis实例。
备注:zookeeper不需要,是因为zk是基于CP原则实现的,而且是顺序节点,那么客户端要么看不到,要么看到的是正确数据
1.客户端获取当前时间,以毫秒为单位
2.客户端尝试获取n个节点的锁,每个节点获取锁的方式同上面单机redis
3.客户端计算计算在获取锁的过程中总共花费时间,时间小于超时时间且获得了半数以上的锁,则获取锁成功
4.更新锁的超时时间(预定义超时时间-获取锁的时间)
5.如果获取锁失败,则删除所有的锁
参考文档:
https://www.jianshu.com/p/a1ebab8ce78a
https://www.cnblogs.com/ldws/p/12155003.html