4、问题3中,因为getValue和deleteKey是两个命令,也就不是原子性操作,意味着当一个客户端执行完getKey方法并在执行deleteKey方法之前,也就是在这2个方法执行之间,其他客户端是可以执行其他命令的。考虑这样一种情况,在客户端A执行完getKey方法,并且该key对应的值也等于先前的随机值的时候,接下来客户端A将会执行deleteKey方法。假设由于网络或其他原因,客户端A执行getKey方法之后过了1秒钟才执行deleteKey方法,那么在这1秒钟里,该key有可能也会因为过期而被Redis清除了,这样一来另一个客户端,姑且称之为客户端B,就有可能在这期间获取到锁,然后接下来客户端A就执行到deleteKey方法了,如此一来就又出现误释放别的客户端申请的锁的问题了。


高并发解决方法----分布式Redis锁

1、分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

1、互斥性。在任意时刻,只有一个客户端能持有锁。
2、不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
3、 具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
4、 只允许解自己的锁。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了

2、需要了解的redis命令

Redis是单线程操作,线程安全
2.1 setnx (set if not exists) 例如:setnx akey avalue =>1; setnx akey avalue =>0;
2.2 setex(过期时间秒级)例如:setex akey 10 avalue;有效期10秒
2.3 psetex(过期时间毫秒级) 例如: psetex akey 10000 avalue;有效期10秒
Redis 2.6.12开始set指令可以执行上述操作:
例如Set key value NX PX milliseconds;//成功返回OK,失败返回NULL;

3、执行逻辑:

3.1 获取并发锁

3.2 判断是否取到锁

3.3 有锁:执行业务方法;无锁:排队/退出

3.4 删除并发锁

Redis的锁怎么解决并发问题 redis 并发锁_分布式

4、加锁/解锁

4.1 循环:判断等待时间是否>=0;
4.2 set方法设置key/value/过期时间,判断是否成功;成功:返回true
4.3 如果等待时间==0,直接返回失败
4.4 睡眠等待100毫秒,等待时间递减;循环判断

// 加锁
Public Boolean getLock(Jedis jedis,int timeOut, int expireTime){ 
Long waitTime = 100l;//默认等待100毫秒
While(timeOut>=0){// 循环
If(“OK”.equals(jedis.set(key,UUID,”NX”,”PX”, expireTime))){
Return true;
if (timeout == 0) {//需要等待的时间
return false;
if (timeout < waitTime) {
  waitTime = timeout;
Thread.sleep(waitTime);// 等一会
timeout -= waitTime; 
return false; 
}
// 解锁
public static boolean releaseLock(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"; //LUA脚本
    Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        return false;
}
// 调用并发执行方法
public void execute(*****){
		if(getLock(**)){
			callBack.invoke();
		}else{
			throw new BizException("");
	}catch(Exception e){
	 	throw new BizFailException("");
	}finally{
		releaseLock(**);
}

问答:

1、如果业务执行过程中发生异常,key没有删除,怎么办?

答:给key设置默认过期时间,时间到了自动删除

2、如果使用setnx、psetex指令,这是两个指令,非原子性操作。如果在setnx指令完成后,在psetex指令之前redis宕机了,也会发生死锁?

答:使用set key value NX PX seconds;一条命令,就是原子性操作,避免这种情况。

3、客户端A获取锁的时候设置了key的过期时间为2秒,然后客户端A在获取到锁之后,业务逻辑方法doSomething执行了3秒(大于2秒),当执行完业务逻辑方法的时候,客户端A获取的锁已经被Redis过期机制自动释放了,因此客户端A在获取锁经过2秒之后,该锁可能已经被其他客户端获取到了。当客户端A执行完doSomething方法之后接下来就是执行releaseLock方法释放锁了,但是此时的锁是别人的。如何规避删除那别人的锁?

答:value可以设置一个UUID随机值,删除锁的时候判断UUID是否是当去自己设置的那个值,是的话才删除。

4、问题3中,因为getValue和deleteKey是两个命令,也就不是原子性操作,意味着当一个客户端执行完getKey方法并在执行deleteKey方法之前,也就是在这2个方法执行之间,其他客户端是可以执行其他命令的。考虑这样一种情况,在客户端A执行完getKey方法,并且该key对应的值也等于先前的随机值的时候,接下来客户端A将会执行deleteKey方法。假设由于网络或其他原因,客户端A执行getKey方法之后过了1秒钟才执行deleteKey方法,那么在这1秒钟里,该key有可能也会因为过期而被Redis清除了,这样一来另一个客户端,姑且称之为客户端B,就有可能在这期间获取到锁,然后接下来客户端A就执行到deleteKey方法了,如此一来就又出现误释放别的客户端申请的锁的问题了。

答:使用LUA脚本执行删除操作,一条命令就不存在上述问题,“if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end”; //LUA脚本。这样一条语句就判断删除自己的锁了。

5、 Redis锁的过期时间小于业务的执行时间该如何续期?

答:利用看门狗机制定时检查锁。比如默认情况下,加锁的时间是30秒。如果加锁的业务没有执行完,那么到 30-10 = 20秒的时候,就会进行一次续期。把锁重置成30秒。那这个时候可能又有同学问了,那业务的机器万一宕机了呢?宕机了定时任务跑不了,就续不了期,那自然30秒之后锁就解开了呗。

mysql下载安装教程5.7 mysql下载安装教程8.0.32

MySQL安装详解前言对于无论是刚开始接触编程的小伙伴,还是有了几年工作经验的程序猿(程序媛)来讲,数据库的安装一直都是一个比 较复杂的过程,安装完成以后可能会记得一段时间,但是等到我们换了一台电脑或者重装了系统,发现之前安装的MySQL 数据库不好用了,然后自己也忘了具体的安装过程,只好求救于各个安装教程,我也一样,但是安装过程出现了很多问题, 特此记录一下具体的安装过程,以便同伴采用。废话少说

python提前进入下一次循环 python提前结束循环

先看基本解释 (1)continue和break都用于循环语句中,python中主要的循环结构有for循环和while循环两种。 (2)执行break语句,会使得break语句所属层次的循环提前结束。 (3)执行continue语句,作用是提前结束本次循环,提前进入下一次循环。进行代码实验 1.#代码一: a = [1, 2, 3] for i in a: print("铅笔") 4、问题3中,因为getValue和deleteKey是两个命令,也就不是原子性操作,意味着当一个客户端执行完getKey方法并在执行deleteKey方法之前,也就是在这2个方法执行之间,其他客户端是可以执行其他命令的。考虑这样一种情况,在客户端A执行完getKey方法,并且该key对应的值也等于先前的随机值的时候,接下来客户端A将会执行deleteKey方法。假设由于网络或其他原因,客户端A执行getKey方法之后过了1秒钟才执行deleteKey方法,那么在这1秒钟里,该key有可能也会因为过期而被Redis清除了,这样一来另一个客户端,姑且称之为客户端B,就有可能在这期间获取到锁,然后接下来客户端A就执行到deleteKey方法了,如此一来就又出现误释放别的客户端申请的锁的问题了。