浅谈分布式锁--简介篇
1、什么是分布式锁(分布式系统用到的锁):
分布式锁,是单机锁的一种扩展,主要是为了锁住分布式系统中不同机器代码的物理块或逻辑块。以此保证不同机器之间的逻辑一致性。
在集群等多服务器中经常使用到同步处理一下业务,这是普通的事务是满足不了业务需求,需要分布式锁。
分布式锁是控制分布式系统或不同系统之间共同访问共享资源的一种锁实现,如果不同的系统或同一个系统的不同主机之间共享了某个资源时,往往需要互斥来防止彼此干扰来保证一致性。
2、分布式锁需要解决的问题:
1、互斥性:任意时刻,只能有一个客户端获取锁,不能同时有两个客户端获取到锁。
2、安全性:锁只能被持有该锁的客户端删除,不能由其它客户端删除。
3、死锁:获取锁的客户端因为某些原因(如down机等)而未能释放锁,其它客户端再也无法获取到该锁。
4、容错:当部分节点(redis节点等)down机时,客户端仍然能够获取锁和释放锁。
3、分布式锁场景:
目前很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。
分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。
在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。
在单机环境中,Java中其实提供了很多并发处理相关的API,但是这些API在分布式场景中就无能为力了。
也就是说单纯的Java Api并不能提供分布式锁的能力。所以针对分布式锁的实现目前有多种方案。
4、分布式锁的实现,目前比较常用的有以下几种方案:
基于数据库实现分布式锁
基于缓存(redis,memcached,tair)实现分布式锁
基于Zookeeper实现分布式锁
5、分布式锁的设计应该是怎么样的?
可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行。
这把锁要是一把可重入锁(避免死锁)。
这把锁最好是一把阻塞锁。
有高可用的获取锁和释放锁功能。
获取锁和释放锁的性能要好。
具备锁失效机制,防止死锁。
6、分布式锁实现原理总结:
6.1 分布式锁的实现(主要涉及那些东西,有3方面)
1.如何获取到锁。
2.如何释放得到的锁。
3.如何得知锁被释放了。
6.2 如何获取到锁
能够提供一种方式,多个客户端并发操作,只能有一个客户端能满足相应的要求,获得相应的锁。
数据库的for update的sql语句、或者插入一个含有唯一约束的数据等。
redis的setnx等。
ZooKeeper的求最小节点的方式。
这些都可以保证只能有一个客户端获取到了锁。
6.3 如何释放得到的锁
场景一般有2种情况:
1 正常情况下的释放锁
2 异常情况下如何释放锁(即释放锁的操作没有被执行,如挂掉、没执行成功等原因)
数据库在查询语句后面增加for update,数据库会在查询过程中给数据库表增加排他锁,通过connection.commit()操作来释放锁。使用这种方式,服务宕机之后数据库会自己把锁释放掉。
redis正常情况下释放锁是删除lock_key,异常情况下,只能通过lock_key的超时时间了。
ZooKeeper正常情况下释放锁是删除临时节点,异常情况下,服务器也会主动删除临时节点。
6.4 如何得知锁被释放了
实现方式一般有2种情况:
1 没有获取到锁的客户端不断尝试获取锁(循环等待,比较浪费资源)。
2 服务器端通知客户端锁被释放了。
第二种情况是最优的(客户端所做的无用功最少),如ZooKeeper通过注册watcher来得到锁释放的通知。
而数据库、redis没有办法来通知客户端锁释放了,那客户端就只能傻傻的不断尝试获取锁了。
7、分布式锁实现方式优缺点:
数据库锁:
优点:直接使用数据库,使用简单。
缺点:分布式系统大多数瓶颈都在数据库,使用数据库锁会增加数据库负担。
缓存锁(redis,memcached,tair):
优点:性能高,实现起来较为方便,在允许偶发的锁失效情况,不影响系统正常使用,建议采用缓存锁。
缺点:通过锁超时机制不是十分可靠,当线程获得锁后,处理时间过长导致锁超时,就失效了锁的作用。
zookeeper锁:
优点:不依靠超时时间释放锁;可靠性高;系统要求高可靠性时,建议采用zookeeper锁。
缺点:性能比不上缓存锁,因为要频繁的创建节点删除节点。