精华内容
下载资源
问答
  • setnx 问题
    千次阅读
    2020-03-31 09:44:18

    最近遇到一个比较奇怪的问题,在秒杀的时候,redis的分布式锁竟然没有锁住,在并发的时候,事务开启的太长,没有拿到相应的数据,从而进行的脏读,以及脏写。我在 为了解决这个问题,运用lua 脚本去操作锁

    好了,现在开始 上代码:

    1.设置 redis 锁

    Boolean setResult =false;
    //redis 锁
     String key = "lock:exchange"+userId+"commodity"+shopCommodityId;
    //uuId 作为 请求id
     String requestId = UUID.randomUUID().toString() ;
    //进行锁  操作
     setResult =   distributedLock.lock(key, requestId,60000);
     log.info("用户:"+userId+"--锁--返回结果:"+setResult);

    2.redis 锁工具类

    public interface DistributedLock {
        /**
         * 获取锁
         * @param requestId 请求id uuid
         * @param key 锁key
         * @param expiresTime 失效时间 ms
         * @return 获取锁成功 true
         */
        boolean lock(String key,String requestId,int expiresTime);
    
        /**
         * 释放锁
         * @param key 锁key
         * @param requestId 请求id uuid
         * @return 成功释放 true
         */
        boolean releaseLock(String key,String requestId);
    }
    public class DistributedLockImpl implements DistributedLock{
    
        public DistributedLockImpl(StringRedisTemplate redisTemplate){
            this.redisTemplate = redisTemplate;
        }
    
        private StringRedisTemplate redisTemplate;
        
        //lua 脚本编写
        private static String lockScript = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then  return redis.call('expire',KEYS[1],ARGV[2])  else return 0 end";
    
        private static String releaseLockScript = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
    
        @Override
        public boolean lock( String key,String requestId, int expiresTime) {
            DefaultRedisScript<Long> longDefaultRedisScript = new DefaultRedisScript<>(lockScript, Long.class);
            Long result = redisTemplate.execute(longDefaultRedisScript, Collections.singletonList(key), requestId,String.valueOf(expiresTime));
            return result == 1;
        }
    
        @Override
        public boolean releaseLock(String key, String requestId) {
            DefaultRedisScript<Long> longDefaultRedisScript = new DefaultRedisScript<>(releaseLockScript, Long.class);
            Long result = redisTemplate.execute(longDefaultRedisScript, Collections.singletonList(key), requestId);
            return result == 1;
        }
    }
    

    3.根据返回锁的结果 进行操作;

    4.最后进行锁的释放,我建议在 finally 里面进行操作,无论如何都会进行锁的释放。

    更多相关内容
  • setNX遇到的问题

    千次阅读 2019-10-31 14:57:04
    先说一次遇到的问题 关于redis分布式锁的问题, 先来一个遇到问题的代码 /** 错误示范 */ public boolean lock(final String lockKey) { for(String lock : locks) { if (lock.equals(lockKey)) { retur.....

    先说一次遇到的问题

    关于redis分布式锁的问题,

    先来一个遇到问题的代码

    /**
    错误示范
    */
    
        public boolean lock(final String lockKey) {
    
            for(String lock : locks) {
                if (lock.equals(lockKey)) {
                    return true;
                }
            }
    
             Long i = jedisClient.execute(new JedisAction<Long>() {
    
                 @Override
                 public Long doAction(Jedis jedis) {
                    //这个地方上锁
                     Long i = jedis.setnx(lockKey, "1");
    
                     if( i == null ) {
                         return 0L;
                     }
    
                     if(i == 1) {
                         //这个地方设置有效时间
                         jedis.expire(lockKey, LOCKKEY_EXPIRE_TIME);
                         locks.add(lockKey);
    
                         if(LOGGER.isInfoEnabled()) {
                             LOGGER.info("添加锁 [{}]",lockKey);
                         }
                     }
                     return i;
                 }
             });
    
            return i == 1;
    
        }

    以上代码的问题在于 把上锁和设置有效时间分开了,所以导致停服的时候正好上了锁,但是没有设置有效时间,所以一直有问题

     

    那么redis应该如何正确有效的上锁呢

    目前的setnx完全可以在一个方法里面实现有效时间和key

     

    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import redis.clients.jedis.JedisPool;
    
    /**
     * 基于redis setnx的 分布式锁 实, 前提是所有的锁都要有锁定时间.
     * 获取锁的时候,需要指定value,在unlock的时候,会根据value判断是否remove
     */
    public class RedisLockUtil {
        private static final String LOCK_PREFIX = "LOCK";
        private static final Integer DEFAULT_LOCK_TIME = 10;// 默认锁定时间秒
        private static final Long DEFAULT_SLEEP_TIME = 100L;// 默认sleep时间,100毫秒
        private static Logger logger = LoggerFactory.getLogger(RedisLockUtil.class);
    
        /**
         * 获取缓存的value,随机值,使不同的锁value不同 (多服务器可以使用redis时间+客户端标识等)
         *
         * @return
         */
        public static String getLockValue() {
            int random = (int) ((Math.random() * 9 + 1) * 100000);
            long now = System.currentTimeMillis();
            return String.valueOf(now) + String.valueOf(random);
        }
    
        /**
         * 获取锁,如果失败,自动重试
         *
         * @param key
         * @param value
         * @return
         */
        public static boolean lock(JedisPool jedisPool, String key, String value) {
            return lock(jedisPool, key, value, DEFAULT_LOCK_TIME);
        }
    
        /**
         * 获取锁,如果失败,自动重试
         *
         * @param key
         * @param value
         * @param lockTime 获取成功后的锁定时间
         * @return
         */
        public static boolean lock(JedisPool jedisPool, String key, String value, int lockTime) {
            return lock(jedisPool, key, value, lockTime, true);
        }
    
        private static boolean lock(JedisPool jedisPool, String key, String value, int lockTime, boolean reTry) {
            return lock(jedisPool, key, value, lockTime, reTry, 0, false, 0);
        }
    
        /**
         * 获取锁,如果失败,直接返回false
         *
         * @param key
         * @param value
         * @return
         */
        public static boolean tryLock(JedisPool jedisPool, String key, String value) {
            return tryLock(jedisPool, key, value, DEFAULT_LOCK_TIME);
        }
    
        /**
         * 获取锁,如果失败,直接返回false
         *
         * @param key
         * @param value
         * @param lockTime 获取成功后的锁定时间
         * @return
         */
        public static boolean tryLock(JedisPool jedisPool, String key, String value, int lockTime) {
            return lock(jedisPool, key, value, lockTime, false);
        }
    
        /**
         * 尝试获取锁,如果获取失败,重试,直到成功或超出指定时间
         *
         * @param key
         * @param value
         * @param lockTime 获取成功后的锁定时间
         * @param timeOut  获取锁等待超时时间
         * @return
         */
        public static boolean tryLock(JedisPool jedisPool, String key, String value, int lockTime, long timeOutMillis) {
            return lock(jedisPool, key, value, lockTime, true, 0, true, timeOutMillis);
        }
    
        /**
         * 释放锁,key对应的value于参数value一致,才删除key
         *
         * @param key
         * @param value
         */
        public static boolean unlock(JedisPool jedisPool, String key, String value) {
            String fullKey = getFullKey(key);
            boolean success = JedisUtil.unlock(jedisPool, fullKey, value);
            if (success) {
                logger.info("unlock success ; key:" + key + ",value:" + value);
            } else {
                logger.info("unlock failed ; key:" + key + ",value:" + value);
            }
            return success;
        }
    
        /**
         * 获取锁
         *
         * @param key
         * @param value
         * @param lockTime      锁定时间
         * @param reTry         失败是否重试
         * @param curTryTime    当前尝试次数
         * @param needTimeOut   是否需要判断超时时间
         * @param timeOutMillis 尝试超时时间(毫秒)
         * @return
         */
        private static boolean lock(JedisPool jedisPool, String key, String value, int lockTime, boolean reTry, int curTryTime,
                                    boolean needTimeOut, long timeOutMillis) {
    
            logger.info(Thread.currentThread().getName() + ",lock come in ; key:" + key + ",value:" + value
                    + ",lockTime:" + lockTime + ",reTry:" + reTry + ",curTryTime:" + curTryTime + ",needTimeOut:"
                    + needTimeOut + ",timeOutMillis:" + timeOutMillis);
    
            curTryTime++;
            String fullKey = getFullKey(key);
    
            // setnx 并设置超时时间
            boolean success = JedisUtil.setnx(jedisPool, fullKey, value, (long) lockTime * 1000);
            // 获取成功,直接返回
            if (success) {
    
                logger.info("lock success ; key:" + key + ",value:" + value + ",lockTime:" + lockTime + ",reTry:"
                        + reTry + ",curTryTime:" + curTryTime + ",needTimeOut:" + needTimeOut + ",timeOutMillis:"
                        + timeOutMillis);
    
                return true;
            }
    
            // 获取失败,不需要重试,直接返回
            if (!reTry) {
                logger.info("lock failed ; key:" + key + ",value:" + value + ",lockTime:" + lockTime + ",reTry:"
                        + reTry + ",curTryTime:" + curTryTime + ",needTimeOut:" + needTimeOut + ",timeOutMillis:"
                        + timeOutMillis);
    
                return false;
            }
    
            // 获取失败, 且已超时,返回
            if (needTimeOut && timeOutMillis <= 0) {
                logger.info("lock failed ; key:" + key + ",value:" + value + ",lockTime:" + lockTime + ",reTry:"
                        + reTry + ",curTryTime:" + curTryTime + ",needTimeOut:" + needTimeOut + ",timeOutMillis:"
                        + timeOutMillis);
    
                return false;
            }
    
            // 获取sleep时间
            long sleepMillis = getSleepMillis(needTimeOut, timeOutMillis);
    
            // sleep后重新获取锁
            sleep(sleepMillis);
    
            // 大于100次,打印warning日志
            if (curTryTime > 100) {
                logger.warn("lock warning ; key:" + key + ",value:" + value + ",lockTime:" + lockTime + ",reTry:"
                        + reTry + ",curTryTime:" + curTryTime + ",needTimeOut:" + needTimeOut + ",timeOutMillis:"
                        + timeOutMillis);
    
            }
    
            return lock(jedisPool, key, value, lockTime, reTry, curTryTime, needTimeOut, timeOutMillis);
        }
    
        private static long getSleepMillis(boolean needTimeOut, long timeOutMillis) {
            long sleepMillis = DEFAULT_SLEEP_TIME;
            if (needTimeOut) {
                timeOutMillis = timeOutMillis - DEFAULT_SLEEP_TIME;
                if (timeOutMillis < DEFAULT_SLEEP_TIME) {
                    sleepMillis = timeOutMillis;
                }
            }
            return sleepMillis;
        }
    
        private static void sleep(long sleepMillis) {
            try {
                Thread.sleep(sleepMillis);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static String getFullKey(String key) {
            return LOCK_PREFIX + ":" + key;
        }
    }

     

    展开全文
  • 比较熟悉redis或者说,redis的用法比较简单,所以查了一下redis使用setnx实现分布式锁的方式。其中有一篇文章搜索到的次数最多,多到我不知道哪个是原创文章,就贴一下看到的链接吧...

    最近在工作中用到了分布式锁,然后查了很多分布式锁的实现方式。比较熟悉redis或者说,redis的用法比较简单,所以查了一下redis使用setnx实现分布式锁的方式。其中有一篇文章搜索到的次数最多,多到我不知道哪个是原创文章,就贴一下看到的链接吧https://blog.csdn.net/lihao21/article/details/49104695。

    reids > setnx(key,value)  //设置key.
    
    redis > delete(key)  //将key删除
    

    当key存在的时候,设置会失败,返回-1。当key不存在的时候,设置成功,返回0。

    实现方式一:

    lock_key = "distribute_lock"
    def get_lock():
          while True:
              lock = redis_client.setnx(lock_key,1)
              if lock: # 设置成功
                	break
              else:
               	 time.sleep(0.5)  #如果没有获取成功,等待0.5继续获取,当然这个地方你也可以设置重试次数。
    	return True
    if get_lock():
        # do your work,处理临界资源
        redis_client.delete(lock_key) //为防止死锁,处理完临界资源要及时释放锁。
    

    可以看一下这个代码有什么问题?

    问题出在了,最后锁的释放,假如在处理临界资源的过程中,进程挂掉了或者在执行删除操作的时候redis链接断掉了,那么这个分布式锁将永远得不到释放,进而产生死锁。所以接下来的优化是如果进程挂掉了,能够及时的释放锁,你想到了什么?超时机制。

    实现方式二:

    为了避免出现方式一的问题,所以加入了超时机制。

    setnx(key, <current Unix time + lock timeout + 1>)
    

    本质就是将key的value值设置成一个超时时间,按照方式一流程:

    lock_key = "distribute_lock"
    def get_lock():
        while True:
          lock = redis_client.setnx(lock_key,<now_time+ lock timeout + 1>) # 直接设置成功
          if lock: # 设置成功
          		break
          now_time = <current Unix time + lock timeout + 1>
          lock_time_out = redis.client.get(lock_key)
          if now_time > lock_time_out: #检查是否超时
          	  # 如果已经超时
              redis_client.delete(lock_key) # 将这个key删除,进行重新设置。
              lock = redis_client.setnx(lock_key,<now_time+ lock timeout + 1>)
          if lock: # 设置成功
          		break
          else:
          	time.sleep(0.5)  #如果没有获取成功,等待0.5继续获取,当然这个地方你也可以设置重试次数。
    	return True
    if get_lock():
        # do your work,处理临界资源
        redis_client.delete(lock_key) //为防止死锁,处理完临界资源要及时释放锁。
    

    继续思考一下,这又会出现什么问题?

    1. 假如进程p1获取到了锁,同时进程p2和p3在不断的检测锁是否已经超时。
    2. 然后p1进程挂掉了,没有及时删除锁。
    3. 过了一段时间,p2和p3同时检测到了这个锁已经超时,即程序中now_time > lock_time_out都成立。
    4. 首先p2将锁删除了,然后将锁设置了超时时间,致此p2获取到了锁。
    5. p3也检测到了锁超时了,只不过它执行速度比慢,直接将锁删除了,实际上p3删除的是p2设置的锁,这个时候问题就出现了,p3和p2都获取到了锁。

    实现方式三

    实现方式二的关键问题是什么?是p3在删除锁的时候,没有检查是否又有新的进程获取到了该锁。
    为了避免这种情况,p3在执行set操作的时候,用这个命令:

    getset(lock_key,now_time+lock timeout + 1)
    

    这个命令会返回旧,然后设置成新的值。在set之前判断一下当前时间是否大于lock_key的旧值。如果大于,说明已经超时,获取到了锁。假如再次出现上面的情况,p2和p3同时检测到锁的超时,然后p2删除、并获取到了锁。p3执行getset操作,然后当前时间和lock_key的旧值(p2设置的)比较,当前时间小于旧值。获取锁失败,继续下一轮的等待。

    其次最后删除的时候,也不能像前几次一样直接删除。要先判断一下,当前时间小于锁的超时时间的话在删除。避免删除其他进程设置的锁。程序如下:

    def get_lock():
        LOCK_TIMEOUT = 3
        lock = 0
        lock_timeout = 0
        lock_key = 'distribute_lock'
    
        # 获取锁
        while lock != 1:
            now = int(time.time())
            lock_timeout = now + LOCK_TIMEOUT + 1
            lock = redis_client.setnx(lock_key, lock_timeout)
            if lock == 1 or (now > int(redis_client.get(lock_key))) and now > int(redis_client.getset(lock_key, lock_timeout)):
                break
            else:
                time.sleep(0.001)
    if get_lock():
        # dou your lock.处理临界资源()
    
        # 释放锁
        now = int(time.time())
        if now < lock_timeout:
            redis_client.delete(lock_key)
    

    继续看,有什么问题?

    1. 为什么要+1 current Unix time + lock timeout + 1 ?
      +1是因为在 Redis 2.4 版本中,过期时间的延迟在 1 秒钟之内 —— 也即是,就算 key 已经过期,但它还是可能在过期之后一秒钟之内被访问到,而在新的 Redis 2.6 版本中,延迟被降低到 1 毫秒之内。

    2. 这段代码问题在哪里?1) 由于是客户端自己生成过期时间,所以需要强制要求分布式下每个客户端的时间必须同步。 2)当锁过期的时候,如果多个客户端同时执行jedis.getSet()方法,那么虽然最终只有一个客户端可以加锁,但是这个客户端的锁的过期时间可能被其他客户端覆盖。3) 锁不具备拥有者标识,即任何客户端都可以解锁。

    (1)首先第一个问题是存在的,但是一般对于同一个分布群的项目,时间肯定是同步的,而且你见过哪台机器时间是用本地时间的?一般都是联网同步互联网全球设定的时区时间。 (2)Redis是单线程的,所以不存在你说的这种情况。是不可能同时执行的。即使客户端A,B,C 在同一时刻(精确到纳秒)发送了getset给redis。redis也会按照队列顺序依次执行。因此绝对保证只有一个客户端获得锁并进行业务处理最后释放锁,其他客户端一定会返回一个大于当前时间的结果,从而导致或许锁失败,也就不可能出现其他任何客户端能够解锁。你可能会说,“同时”。但是你说“同时”的时候,我就知道,兄弟你对redis的底层没有了解到这一层面,redis是强制单线程的。除非你改人家源码,就算你改人家源码,如果你改了人家的单线程设计理念,就等于说你不赞同redis作者的理念。然而,redis的单线程,却没有影响它的高性能。

    1. 实践中我发现的问题.
    if lock == 1 or (now > int(redis_client.get(lock_key))) and now > int(redis_client.getset(lock_key, lock_timeout))
    

    会出现bug,因为当锁被删除之后,如果正好处于redis_client.get(lock_key)检查的时候,这个时候锁被删除了,所以redis_client.get(lock_key)为None,int(None)会出现类型转换错误。
    然后这个时候如果为None的话,可以直接赋值为0,这样也不会出现问题,因为本身这个锁就被删除了,和时间超时是一样的,所以提出了优化。

    if lock == 1 or (now > int(redis_client.get(lock_key) or 0)) and now > int(redis_client.getset(lock_key, lock_timeout) or 0)
    

    其次为了便于使用,我还优化成了一个类:

    class DistributeLock(object):
    
        def __init__(self, lock_key=None, lock_timeout=2):
            """
            :param lock_key: 分布式锁的key
            :param lock_timeout: 锁的超时时间
            """
            self.LOCK_TIMEOUT = lock_timeout
            self.lock = 0
            self.lock_timeout = 0
            self.lock_key = lock_key  #  # 分布式锁的key
            self.redis_client = #初始化redis客户端。
    
        def __enter__(self):
            # 获取锁
            while self.lock != 1:
                now = int(time.time())
                self.lock_timeout = now + self.LOCK_TIMEOUT + 1
                lock = self.redis_client.setnx(self.lock_key, self.lock_timeout)
                if lock == 1 or (now > int(self.redis_client.get(self.lock_key) or 0)) and now > int(
                        self.redis_client.getset(self.lock_key, self.lock_timeout) or 0):
                    break
                else:
                    time.sleep(0.001)
    
        def __exit__(self, ex_type, ex_value, traceback):
            # 释放锁
            now = int(time.time())
            if now < self.lock_timeout:
                self.redis_client.delete(self.lock_key)
    # 使用方式:
    	with DistributeLock(lock_key="your_distribute_lock",lock_timeout=3):
        	# do your work.
            do_work() # 不用在关心锁的获取或者释放。
    

    其实以上,还有有些问题:就是没有考虑redis挂掉或者主从切换的情况,后续再更新。

    关注我,让我们一起成长。

    在这里插入图片描述

    展开全文
  • Redis:分布式锁setnx 关键词 获取锁:原子性操作 set方法(推荐),谁set成功谁获取到锁(过期时间,避免死锁) 释放锁:redis+lua脚本实现 问题1:主从架构,主机宕机,重复获得锁问题(可以使用红锁解决99%,...

    Redis:分布式锁setnx(只 实现了 互斥性和容错性)


    关键词

    • 同时在redis上创建同一个key,只有一个能成功,即获得锁
    • 获取锁:原子性操作 set方法(推荐),谁set成功谁获取到锁(过期时间,避免死锁)
    • 释放锁:redis+lua脚本实现
    • 问题1(同一性):主从架构,主机宕机,重复获得锁问题(可以使用红锁解决99%,降低重复获取概率,redis在分布式环境下是ap模型)
      红锁:三个节点,往两个节点上set成功,才能获取锁
    • 问题2(续租):无法续租问题(使用Redisson的看门狗进行续租)
    • 问题3:(重入性)

    一、实现原理

    共享资源 互斥
    共享资源 串行化

    分布式应用中使用锁:(多进程多线程)

    • 分布式锁是控制分布式系统之间同步访问共享资源的一种方式
    • 利用Redis的单线程特性对共享资源进行串行化处理

    单应用中使用锁:(单进程多线程)

    • synchronized、ReentrantLock

    二、实现方式

    2.1 获取锁

    方式1 (使用set命令实现)推荐

    /**
    * 使用redis的set命令实现获取分布式锁
    * @param lockKey  可以就是锁
    * @param requestId 请求ID,保证同一性  uuid+threadID
    * @param expireTime 过期时间,避免死锁
    * @return
    */
    public  boolean getLock(String lockKey,String requestId,int expireTime) {
    		// NX: 保证互斥性
        // hset  原子性操作 只要lockKey有效 则说明有进程在使用分布式锁
    	String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);
    	
    	if("OK".equals(result)) {
    		return true;
    	}
    	return false;
    }
    

    方式2 (使用setnx命令实现) – 并发会产生问题

    public  boolean getLock(String lockKey,String requestId,int expireTime) {
    	Long result = jedis.setnx(lockKey, requestId);  //如果挂了,没有设置expire,别人永远无法获取锁
    	
    	if(result == 1) {
    	  //成功设置 进程down 永久有效  别的进程就无法获得锁
    		jedis.expire(lockKey, expireTime);
    		return true;
    	}
    	return false;
    }
    

    2.2 释放锁

    方式1 (del命令实现) – 并发

    /**
    * 释放分布式锁
    * @param lockKey
    * @param requestId
    */
    public static void releaseLock(String lockKey,String requestId) {
       
      if (requestId.equals(jedis.get(lockKey))) { //并发会有问题
        jedis.del(lockKey);
     }
    }
    

    问题:如果调用jedis.del()方法的时候,这把锁已经不属于当前客户端的时候会解除他人加的锁。

    那么是否真的有这种场景?答案是肯定的,比如客户端A加锁,一段时间之后客户端A解锁,在执行jedis.del()之前,锁突然过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。

    方式2 (redis+lua脚本实现)–推荐

    public static boolean releaseLock(String lockKey, String requestId) {
      // 获取到你自己的锁,则删除 ('get', KEYS[1]) == ARGV[1]
    	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 (result.equals(1L)) {
    		return true;
    	}
    	return false;
    }
    

    三、存在问题

    单机

    • 无法保证高可用

    主–从

    • 无法保证数据的强一致性,在主机宕机时会造成 锁的重复获得。(红锁方案解决)
      在这里插入图片描述

    无法续租

    • 超过expireTime后,不能继续使用

    四、本质分析

    CAP模型分析

    在分布式环境下不可能满足三者共存,只能满足其中的两者共存,在分布式下P不能舍弃(舍弃P就是单机了)。
    所以只能是CP(强一致性模型)和AP(高可用模型)。

    分布式锁是CP模型,Redis集群是AP模型。 (base)
    Redis集群不能保证数据的随时一致性,只能保证数据的最终一致性

    为什么还可以用Redis实现分布式锁?

    与业务有关

    • 当业务 不需要数据强一致性 时,比如:社交场景,就可以使用Redis实现分布式锁
    • 当业务必须要数据的强一致性,即不允许重复获得锁,比如金融场景( 重复下单,重复转账 )就不要使用,可以使用CP模型实现,比如:zookeeper和etcd。
    展开全文
  • Boolean acquire = connection.setNX(lockKey.getBytes(), String.valueOf(expireAt).getBytes()); if (acquire) { return true; } else { byte[] value = connection.get(lockKey.getBytes()); if (Objects....
  • Redis SETNX

    2021-03-04 11:01:43
    使用Redis的SETNX实现锁机制 SETNX key value SETNX全程Set If Not Exists,表示只有不存在的时候才设置键值对。 只有在键key不存在的情况下才能将key的值设置为value 若键key已经存在则SETNX命令不做任何动作 ...
  • 本篇文章主要介绍了详解使用Redis SETNX 命令实现分布式锁,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 先使用redis的setnx去设置一个互斥锁,当操作成功返回时再进行查询数据库的操作并回设缓存,否则重试get缓存的方法。2.永久不过期:物理不过期,但逻辑过期(程序控制) 缓存雪崩:设置缓存时采用了相同的过期时间,...
  • redis并发问题(setnx事例)

    千次阅读 2021-01-11 23:07:48
    redis中的并发问题使用redis作为缓存已经很久了,redis是以单线程的形式运行的,命令是一个接着一个执行的,一直以为不会存在并发的问题,直到今天看到相关的资料,才恍然大悟~~具体问题实例有个键,假设名称为myNum...
  • redis setNx方法

    2021-02-12 23:48:54
    Redis有一系列的命令,特点是以NX结尾,NX是Not eXists的缩写,如SETNX命令就应该理解为:SET if Not eXists。这系列的命令非常有用,这里讲使用SETNX来实现分布式锁。用SETNX实现分布式锁利用SETNX非常简单地实现...
  • 3、引入redis的setnx命令来处理接口的幂等 修改后的接口代码 @RestController @RequestMapping("student") @Slf4j public class StudentController { @Autowired private RedisTemplate redisTemplate; @...
  • 关于setnx实现锁

    2021-09-19 13:46:34
    分享两则不同业务场景下使用Redis的setnx加锁操作 1、使用Redis来实现递增编号。在redis中存储了当前的编号,为了保证多线程同时获取该编号,加1操作,再写回,不会出现数据不准确的情况,使用setnx对其加锁。加锁...
  • 基于redis的分布式锁, 性能和稳定性都非常好. 但是redis中setnx+expire是非原子操作, 除了用LUA脚本保证实现原子操作, 其实可以直接使用red...
  • 浅谈redis的setnx

    2021-05-19 16:10:58
    在Redis里,所谓SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果,不过很多人没有意识到SETNX有陷阱! 比如说:某个查询数据库的接口,因为调用量比较大,所以加...
  • SETNX命令简介 命令格式 SETNX key value 将 key 的值设为 value,当且仅当 key 不存在。 若给定的 key 已经存在,则 SETNX 不做任何动作。 SETNX 是SET if Not eXists的简写。 返回值 返回整数,具体为 - 1...
  • Redis SETNX 命令

    2021-03-07 02:30:42
    Redis SETNX 命令只有在指定的 key 不存在的情况下,将 key 设置为 value 值,这时等同于 SET 命令;若指定的 key 存在,则什么也不做。SETNX 是“SET if Not eXists”的缩写。命令格式SETNX key value可用版本:>...
  • 转自:https://my.oschina.net/u/1995545/blog/366381Redis有一系列的命令,特点是以NX结尾,NX是Not eXists的...用SETNX实现分布式锁利用SETNX非常简单地实现分布式锁。例如:某客户端要获得一个名字foo的锁,客...
  • 这里jedis的setnx方式为我们解决了这个问题,简单原理是:当A用户先set成功了,那B用户set的时候就返回失败,满足了某个时间点只允许一个用户拿到锁。 锁过期时间: 某个抢购场景时候,如果没有过期的概念,当A用户...
  • 并且利用setnx命令实现了分布式锁,将加锁与解锁的逻辑利用redis实现,避免了使用互斥锁时多台jvm所拥有的监视锁对象不一致的问题 实际使用时如果是单机系统,在利用jdk提供的互斥锁时,需要对互斥锁使用intern()...
  • Redis的SETNX的使用方法

    2021-04-24 17:26:05
    对应给定的keys到他们相应的values上。只要有一个key已经存在,MSETNX一...在 Redis 里,所谓 SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果,不过很多人没有意识...
  • 谈谈Redis中的SetNX

    2021-04-24 17:26:28
    SetNX命令前,先顺带引入下Set命令,由于在Golang开启两个并发协程后,单位时间内读到的有可能是同一个值,因此这对本来就是单线程并发安全的Redis造成了非并发安全的错觉。如下代码所示:Gofunc main() {config:=kv....
  • Redis命令SETNX的使用(包含Java分布式锁实现) 可以参考Redis官网对SETNX命令的介绍: https://redis.io/commands/setnx SETNX命令简介 命令格式 SETNX key value 将 key 的值设为 value,当且仅当 key ...
  • setNX,是set if not exists的缩写,也就是只有不存在的时候才设置,设置成功时返回1,设置失败时返回0。可以利用它来实现锁的效果,但是很多人在使用的过程中都有一些问题没有考虑到。 例如某个查询数据库的接口...
  • 正确地使用Redis的SETNX实现锁机制

    千次阅读 2021-03-04 07:31:55
    setNX,是set if not exists 的缩写,也就是只有不存在的时候才设置, 设置成功时返回 1 , 设置失败时返回 0 。可以利用它来实现锁的效果,但是很多人在使用的过程中都有一些问题没有考虑到。例如某个查询数据库的...
  • 在一些大的互联网平台,通常都会使用定时任务处理一些周期性的...同样会出现并发问题,redis有专门的解决分布式并发问题的方法,就是setnx命令,很好用,下面介绍一下java实现redis分布式锁的方法。 1. Redis S...
  • 一、问题发生 正常情况下,通过 setnx 设置的一个锁,应该会1s后过期(防止并发请求,处理相同逻辑),但是实际发生的情况是,第一次处理下面的逻辑,就抛出异常。说明 setnx 函数返回了 false。 $redis_key_...
  • 分布式锁怎么实现: setnx: 存在的问题:tomcat 突然停了,没有释放锁,会造成死锁。 给redis设置过期时间: 10秒 代码没有执行完。高并发情况下:锁会永久失效。 自己加的锁,被别的线程释放掉了。 redisson 完美...
  • } } 存在问题setnx抢占锁成功,业务代码或者程序在运行过程中宕机了,没有执行锁删除逻辑,就会造成死锁。 解决:设置锁的自动过期,即使没有手动删除,会自动删除; 分布式锁阶段二 使用expire 设置过期时间 /**...
  • 客户端C2使用SETNX命令获取锁 假设客户端C1已经崩溃但是仍然持有锁,所以Redis返回false给客户端C2 客户端C2使用GET命令获取锁并检查锁是否已经过期,如果没有过期,则继续等待一段时间并重新重试 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,351
精华内容 9,740
关键字:

setnx 问题