分布式锁的实现方式

分布式锁的特性:

  1. 互斥性 可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行。
  2. 锁是一把可重入锁(避免死锁)
  3. 不会发生死锁:有一个客户端在持有锁的过程中崩溃而没有解锁,也能保证其他客户端能够加锁
  4. 锁最好是一把阻塞锁(根据业务需求考虑要不要这条)
  5. 有高可用的获取锁和释放锁功能
  6. 获取锁和释放锁的性能要好

分布式锁的实现方式有:数据库锁、redis式的分布式锁、zookeeper分布式锁。

数据库锁:

其实现方式一:

建一张锁表,其表字段大概有id与方法名称(必须对方法名称做唯一性约束)。通过对锁表中的数据进行操作来实现分布式锁。

       如果要操作该资源,就在锁表中新增一条记录;操作完成后就将该记录删除掉。

       这方法有些弊端:

  1.  这种方法依赖数据库比较强,如果数据库是单点的,只要数据库出现问题了,会导致整个业务系统不能用。
  2.   这种方法没有时间失效性,如果delete失败,这条记录就一直会在数据库中,其他线程就无法获得该锁。
  3. 这种方法是非阻塞的,如果insert失败数据库就会直接报错,其他线程没有队列可进入,如果要想获得锁就只能再次触发insert操作。
  4. 这种方法是非重入的,一线程在释放锁之前无法再次获得该锁。

     当然,这些弊端也是有解决办法的:

  1. 数据库单点,就多加一数据库。实现数据库之间的双向同步。
  2. 没有时间失效性,可以用定时任务作批删除操作。
  3. 非阻塞,可以用while循环,知道insert成功返回。
  4. 非重入,可以在数据库中加一字段信息。每次insert之前都判断该信息是否存在,若存在就直接操作。

其实现方式二:

也可以用数据库的排它锁来实现分布式锁,用for update来实现:在查询记录后面用“for update”来加排他锁,加锁后其他线程不可用,用connection.commit()来释放。

这种用排他锁的方式可以解决阻塞和释放锁(时间失效性)的问题,但单点和非重入却没办法解决。

redis分布式锁:

用缓存来实现分布式锁,可以解决数据库单点的问题,因为缓存大多数都是集群的。只不过为了保证分布式锁可用,得同时满足下面四个条件:

  1. 互斥性,顾名思义在任何时候都只能有一个客户端能持有锁。
  2. 不能有死锁,如果客户端在持有锁期间发生崩溃而无法释放锁,也要保证其他客户端能持有该锁。
  3. 具有容错性。只要redis的大部分节点正常运行,客户端就能加锁和解锁。
  4. 对于同一把锁,只能是同一个客户端进行加锁与解锁。

redis的分布式锁,只需要一行代码就可以进行加锁:

jedis.set(String key, String value, String nxxx, String expx, int time)

其中,key是用来表示锁的,因为key具有唯一性;

value一般用requestId,用uuid().toString()即可。这一般用于解锁时,对同一客户端做校验用;

nxxx用“NX”,即set if not exist(如不存在则set,存在则不作任何操作);

expx用“PX”,表示该锁的过期时间,由time来确定;

这条代码可以看出,redis分布式锁可以满足上面的条件。且比数据库分布式锁简单得多,且性能也比较好。

切记:jedis.setnx()jedis.expire()这种方式虽然能加锁,但不具有原子性。因为不能做到同一客户端进行加、解锁,因此使用时要慎重。

zookeeper分布式锁:

用zookeeper的临时有序节点可以实现分布式锁,其实现思路为每个客户端对某个方法加锁,在zookeeper上与该方法对应的节点目录上生成一个临时有序节点即可。

      判断一个方法是否加锁,只需要判断这个有序节点的序号是否是最小的一个。释放锁只需要将临时节点删除即可。

      这实现方式可以解决死锁、非阻塞、非重入的问题,因为zookeeper是集群部署的,同时也解决了单点问题。

      (避免死锁:客户端在zookeeper上创建临时节点,在执行业务期间客户端挂了后,该临时节点回自动删除,不会影响其他客户获得锁;

        阻塞式锁:客户端在zookeeper上创建临时节点时,可以给该节点绑定监听。该节点有任何变化zookeeper都会通知客户端,客户端可以判断该节点的序号是否是最小的,若是则可以执行业务逻辑处理;

        重入式锁:客户端在zookeeper创建节点时,可以把客户端相关信息写入节点中,再次想获取锁时,只需要验证是否是最小序号即可

当然,zookeeper的分布式锁性能不是最好的,因为zookeeper加锁和解锁,都是动态创建节点和删除节点的,都是leader这个角色完成的,然后同步到follower。所以性能不是最好的

 

 

      

你可能感兴趣的:(redis,zookeeper,分布式锁实现)