分布式锁的这三种实现90%的人都不知道( 二 )


占有锁
update t_resoure set state=2, version=2, update_time=now() where method_name='methodName' and state=1 and version=2;
如果没有更新影响到一行数据 , 则说明这个资源已经被别人占位了 。
缺点:
1、这把锁强依赖数据库的可用性 , 数据库是一个单点 , 一旦数据库挂掉 , 会导致业务系统不可用 。
2、这把锁没有失效时间 , 一旦解锁操作失败 , 就会导致锁记录一直在数据库中 , 其他线程无法再获得到锁 。
3、这把锁只能是非阻塞的 , 因为数据的insert操作 , 一旦插入失败就会直接报错 。 没有获得锁的线程并不会进入排队队列 , 要想再次获得锁就要再次触发获得锁操作 。
4、这把锁是非重入的 , 同一个线程在没有释放锁之前无法再次获得该锁 。 因为数据中数据已经存在了 。
解决方案:
1、数据库是单点?搞两个数据库 , 数据之前双向同步 。 一旦挂掉快速切换到备库上 。
2、没有失效时间?只要做一个定时任务 , 每隔一定时间把数据库中的超时数据清理一遍 。
3、非阻塞的?搞一个while循环 , 直到insert成功再返回成功 。
4、非重入的?在数据库表中加个字段 , 记录当前获得锁的机器的主机信息和线程信息 , 那么下次再获取锁的时候先查询数据库 , 如果当前机器的主机信息和线程信息在数据库可以查到的话 , 直接把锁分配给他就可以了 。
2.基于redis实现获取锁使用命令:
SET resource_name my_random_value NX PX 30000
方案:
try{
lock = redisTemplate.opsForValue().setIfAbsent(lockKey, LOCK);
logger.info("cancelCouponCode是否获取到锁:"+lock);
if (lock) {
// TODO
redisTemplate.expire(lockKey,1, TimeUnit.MINUTES); //成功设置过期时间
return res;
}else {
logger.info("cancelCouponCode没有获取到锁 , 不执行任务!");
}
} finally{
if(lock){
redisTemplate.delete(lockKey);
logger.info("cancelCouponCode任务结束 , 释放锁!");
}else{
logger.info("cancelCouponCode没有获取到锁 , 无需释放锁!");
}
}
缺点:
在这种场景(主从结构)中存在明显的竞态:
客户端A从master获取到锁 ,
在master将锁同步到slave之前 , master宕掉了 。
slave节点被晋级为master节点 ,
客户端B取得了同一个资源被客户端A已经获取到的另外一个锁 。 安全失效!
3.基于zookeeper实现 让我们来回顾一下Zookeeper节点的概念:
分布式锁的这三种实现90%的人都不知道文章插图
Zookeeper的数据存储结构就像一棵树 , 这棵树由节点组成 , 这种节点叫做Znode 。
Znode分为四种类型:
1.持久节点 (PERSISTENT)
默认的节点类型 。 创建节点的客户端与zookeeper断开连接后 , 该节点依旧存在。
2.持久节点顺序节点(PERSISTENT_SEQUENTIAL)
所谓顺序节点 , 就是在创建节点时 , Zookeeper根据创建的时间顺序给该节点名称进行编号:
分布式锁的这三种实现90%的人都不知道文章插图
3.临时节点(EPHEMERAL)
和持久节点相反 , 当创建节点的客户端与zookeeper断开连接后 , 临时节点会被删除:
分布式锁的这三种实现90%的人都不知道文章插图
分布式锁的这三种实现90%的人都不知道文章插图
分布式锁的这三种实现90%的人都不知道文章插图
4.临时顺序节点(EPHEMERAL_SEQUENTIAL)
顾名思义 , 临时顺序节点结合和临时节点和顺序节点的特点:在创建节点时 , Zookeeper根据创建的时间顺序给该节点名称进行编号;当创建节点的客户端与zookeeper断开连接后 , 临时节点会被删除 。
Zookeeper分布式锁的原理
Zookeeper分布式锁恰恰应用了临时顺序节点 。 具体如何实现呢?让我们来看一看详细步骤:
获取锁
首先 , 在Zookeeper当中创建一个持久节点ParentLock 。 当第一个客户端想要获得锁时 , 需要在ParentLock这个节点下面创建一个临时顺序节点 Lock1 。
分布式锁的这三种实现90%的人都不知道文章插图
之后 , Client1查找ParentLock下面所有的临时顺序节点并排序 , 判断自己所创建的节点Lock1是不是顺序最靠前的一个 。 如果是第一个节点 , 则成功获得锁 。
分布式锁的这三种实现90%的人都不知道文章插图
这时候 , 如果再有一个客户端 Client2 前来获取锁 , 则在ParentLock下载再创建一个临时顺序节点Lock2 。