什么是分布式锁?简单来说,就是存在于分布式系统中的锁,其目的是保证共享资源的一致性。
分布式锁三种实现方式
1、基于数据库实现
悲观锁
每次都认为别人会更新数据,所以拿到数据后就会加锁。 在多个事务并发执行时,只要某个事务拿到锁之后,此时其他事务就要等到该事务执行完成,其他事务才能对该数据进行修改操作。
乐观锁
每次拿数据的时候都认为别人不会修改数据,所以不上锁,但是在写操作的时候,会判断这个数据有没有被修改。通常通过版本号来控制。
常用实现方式:当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据
2、基于redis实现
SET KEY VALUE [EX seconds] [PX milliseconds] [NX|XX]
- NX 仅在键不存在时设置键
- XX 只有在键已存在时才设置
通过SETNX key 命令加锁,为该key添加一个过期时间,防止死锁;value值可以设置为UUID ,在释放锁前先判断,判断当前锁的UUID是否与传入的UUID值相等,保证谁加的锁,谁才能释放。最后使用DEL命令删除key达到释放锁的目的。
3、基于zookeeper
ZooKeeper保证了数据的强一致性这一特性,根据这一特性,有两种实现方式:
1、利用节点名称的唯一性来实现
并发请求进来时,所有请求都创建同一个节点lock/lockKey
,最终只有一各创建成功,创建成功的即获得锁。当任务执行完成后,通过删除几点的方式达到释放锁的目的。当其他的服务获取锁失败后,会把自己的监听注册到zookeeper。
2、通过临时顺序节点
并发请求进来时,在 /lock目
录下创建临时顺序节点(/lock/lockKey/001),创建节点前先获取/lock
目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前节点顺序号最小,即获得锁;当任务执行完成后就删除当前节点,即为释放锁。一旦客户端获取到锁之后突然挂掉,那么这个临时节点就会自动删除掉。其他客户端就可以再次获得锁。避免了死锁。
4、三种方式优缺点
基于数据库
优点:实现简单,直接使用数据库
确点:容易锁表
基于redis
优点:reids性能好,redis有命令支持,实现方便
缺点:过期时间不好把控,过长或过短都会影响性能
基于zookeeper
优点:zookeeper具有分布式强一致性。锁的模型健壮、简单易用
缺点:一般项目中没有引入ZK,需要额外的添加外部依赖才能实现
总结
适合项目需要的才是最好的,追求速度效率的话就用redis,要安全可靠的话就用zookeeper