分布式锁学习

今天学习了慕课网的教程,做一下总结

为了解决什么问题
解决分布式系统中并发数据的问题,当数据库锁和其他手段无法解决多实例部署的问题时准备的。
比如说:某个数据库资源全实例2000个只能一个实例操作,而且需要多条件判断(假设2000个实例访问同一个数据库),这些判断不能引发数据库行级锁,这是只能使用分布式锁进行资源获取操作。行级锁的时机:事务开启之后修改数据库数据开始到事务提交结束。
Redis锁
什么原理呢?利用Redis的单线程在先判断再执行操作的可以原子性执行的原理,而且性能好
比如说:setnx在存在key的时候会操作失败,而不存在key的时候会操作成功,而判断时原子性的 这是创造锁的绝佳特性
Redis锁造成的死锁问题
为什么会造成死锁,当setnx之后,锁没有被消费,而所有的实例都停机了(直接kill -9 或shutdown.sh)这时重启服务时候所有实例均没有办法获取没有被释放的锁。
解决办法:
1.@PreDestory 这是tomcat容器在自爆之前会默认调用的方法
但是这样如果我有10亿个锁,关闭锁要好久,而且无法处理kill-9造成的状况
最终的分布式锁代码
他是什么原理呢?
1、首先 我先获取锁当获取锁成功的时候直接执行逻辑,但是这里不考虑一个业务的执行时间远大于超时时间的问题
2、 当获取不到锁的时候进行了防死锁处理
他的处理流程是先获取锁的时间戳,如果锁超时了,则我有资格去处理锁,但是同时其他可能有1000个实例同时走到这一步。
我们知道Redis是单线程的是线程安全的所以getSet在分布式下仍然是线程安全的。
1. 当返回的旧值不存在的时候:
该key已经被超时删除了,我可以放心处理逻辑,不考虑线程竞争极端情况导致close
订单时候非锁运行的
2.该key和lockValue中的值相同:
则这个key在当前服务操作前没有被任何其他服务重置过则我可以认为我已经获取了
这个锁,且设置了新的超时时间, 则可以处理逻辑。
3.都不行的时候说明
这个key的值已经被刷新了,一定有其他的服务获得了锁,则这里直接退出了
自己发现的问题:
1. 由于是客户端自己生成过期时间,所以需要强制要求分布式下每个客户端的时间必须同步。
2. 当锁过期的时候,如果多个客户端同时执行jedis.getSet()方法,那么虽然最终只有一个客户端可以加锁, 但是这个客户端的锁的过期时间可能被其他客户端覆盖 。这考虑是不同客户端的超时时间不同的情况,当然这个课程不会有这个问题,超时时间完全一致,覆盖就覆盖了
3. 锁不具备拥有者标识,即任何客户端都可以解锁。会不会造成说一个超时的关订单方法,在其他服务获得锁但没有执行逻辑或执行逻辑一般没有超时,而锁释放了
4.这里面导致任何客户端都会解锁即使这把锁不是它的。

这样也是错误的 因为两条命令不具有原子性 则判断相等之后 线程停留 当前客户端锁过期,其他客户端获得锁,开心的操作,但是当前客户端线程执行锁删了。。。。。


加锁
public class RedisTool {
 
    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
 
    /**
     * 尝试获取分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
 
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
 
        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
 
    }
 
}
解锁
public class RedisTool {
 
    private static final Long RELEASE_SUCCESS = 1L;
 
    /**
     * 释放分布式锁 PS Redis中lua脚本是原子性的
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
 
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
 
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
 
    }
 
}
慕课网课程代码





猜你喜欢

转载自blog.csdn.net/qq1529243239/article/details/81017688