分布式锁的3种实现方式

分布式锁的3种实现方式

1.基于数据库

1.1 悲观锁

   具有强烈的独占性和排他性,认为别人会更新数据,所以拿到数据后就会上锁。悲观锁主要用于保护数据的完整性, 在多个事务并发执行时。只要某个事务拿到锁之后,此时其他事务就要等到该事务执行完成,其他事务才能对该数据进行修改操作。悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。

  使用悲观锁实现分布式锁,在查询语句后面增加for update数据库会在查询过程中给数据库表增加悲观锁

select * from methodLock where method_name=xxx for update;

当任务执行完成后,commit当前事务。来达到释放锁的目的。

注意:method_name这个字段需要加索引,否则会锁表。

1.2 乐观锁

​   每次去拿数据的时候都认为别人不会修改,所以不会上锁,但在做更新操作的时候会判断一下期间别人有没有更新数据,一般是使用version版本号来控制。

实现分布式锁的方式:

   可以创建一张专门的锁表,然后通过操作该表中的数据来实现,当需要操作某个资源时候,可以通过版本号,通过查询当前的版本号,然后执行完任务之后,再判断一下任务前的版本号和任务后的版本号是否一致,如果一致,就将当前版本号+1.
还有一种方式是,当我们要操作某个资源时,就在表中新增一条记录,等到执行完之后将该条数据删除。前提是需要对method_name这个字段做唯一约束,保证有多个事务请求提交到数据库,只有一个能操作成功。操作成功的那个请求获得锁,就可以执行具体的方法,执行完成之后就删除该条记录释放锁。

2.基于Redis

  利用Redis的String(String)数据类型来实现分布式锁。

实现方式1:

   通过SETNX key 命令加锁,并为该key添加一个过期时间,设置过期时间是为了防止死锁;给value设值可以为UUID ,在释放锁前先判断,判断当前锁的UUID是否与传入的UUID值相等。最后使用DEL命令删除key达到释放锁的目的。

实现方式2:

   当并发请求进来是先判断某个key是否存在,如果不存在就set key,并为该key设置一个过期时间防止死锁。当前请亲获得锁,当其他请求进来时发现该key已经存在就等待,当请求执行完成之后就DEL key,达到释放锁的目的。

实现方式3:redlock算法(后面再看看)

实现方式4:Redisson(后面再看看)

3.基于Zookeeper

实现方式1:

   利用节点名称的唯一性来实现。

并发请求进来时,所有请求都创建同一个节点lock/methdlock,最终只有一各创建成功,创建成功的即获得锁。当任务执行完成后,通过删除几点的方式达到释放锁的目的。

实现方式2:

通过临时顺序节点实现。

   并发请求进来时,在 /lock目录下创建临时顺序节点,创建节点前先获取/lock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前节点顺序号最小,即获得锁;当任务执行完成后就删除当前节点,即为释放锁。一旦客户端获取到锁之后突然挂掉,那么这个临时节点就会自动删除掉。其他客户端就可以再次获得锁。避免了死锁。

4.三种方式的优缺点

基于数据库:

  优点:实现方式很好理解;直接使用数据库,不需要在项目里额外的新增组件,实现简单。

  缺点:

    1、有锁表的风险,表中的数据少的时候,数据库会不走索引,导致锁表。
 
    2、如果任务执行时间过长,其他线程会不断的请求,会占用较多的数据库连接资源。

    3、非阻塞,操作失败后,需要轮询,占用cpu资源;

基于Redis:

  优点:reids性能好,redis有较好的命令支持,实现起来方便

  缺点:

   1、如果锁删除失败,使用key的过期时间来控制锁的释放,这个过期时间不好控制。过长或者过短对整个项目的性能都有影响。

   2、非阻塞,操作失败后,需要轮询,占用cpu资源;

   3、再集群模式下,需要考虑的锁失效问题很多,复杂读高。

基于ZooKeeper:

  优点:获取不到锁,只需要注册个监听器即可,不需要不断主动尝试获取锁,性能开销小。zookeeper具有分布式强一致性。锁的模型健壮、简单易用、适合做分布式锁。

  缺点:性能相较于redis差。一般项目中没有引入ZK,需要额外的添加外部依赖才能实现。

5.(总结)选型

 以上几种方式,各有优缺点,都无法做到完美,再实际项目中需要根据实际情况来选择合适的方式;

  从理解的难易程度角度(从低到高):数据库 > 缓存 > Zookeeper

 从实现的复杂性角度(从低到高):Zookeeper >= 缓存 > 数据库

  从性能角度(从高到低):缓存 > Zookeeper >= 数据库

  从可靠性角度(从高到低):Zookeeper > 缓存 > 数据库

 项目追求速度的话就用redis,要安全可靠的话就用ZK

posted @ 2021-05-19 13:07  一勺兔子  阅读(4980)  评论(0编辑  收藏  举报
Live2D