本文介绍: 浅析redis setIfAbsent用法及在分布式锁上的应用同步锁的缺陷

浅析redis setIfAbsent用法及在分布式锁上的应用及同步锁的缺陷

一、业务场景:同步锁的问题分布式锁的应用

1、redis基本命令

(1)SETNX命令(SET if Not eXists

语法:SETNX key value

功能:当且仅当 key存在,将 key 的值设为 value ,并返回1;若给定key 已经存在,则 SETNX 不做任何动作,并返回0。

(2)expire命令

语法expire KEY seconds

功能设置key过期时间。如果key过期,将会被自动删除

(3)DEL命令

语法:DEL key [KEY …]

功能删除给定一个多个 key,不存在key 会被忽略

2、实现同步锁原理

(1)加锁:“锁”就是一个存储redis里的keyvalue对,key是把一组作用字符串来形成唯一标识value其实并不重要,因为只要这个唯一keyvalue存在,就表示这个操作已经上锁。

(2)解锁:既然key-value存在表示上锁,那么释放锁就自然是在redis删除key-value

(3)阻塞、非阻塞阻塞式的实现,若线程发现已经上锁,会在特定时间内轮询锁。非阻塞式的实现,若发现线程已经上锁,则直接返回

(4)处理异常情况:假设当投资操作调用其他平台接口出现等待时,自然没有释放锁,这种情况下加入超时机制,用redisexpire命令为key设置超时时长,过了超时时间redis就会将这个key自动删除,即强制释放锁
在这里插入图片描述
3、使用redis还是出现同步问题

一种可能是,2台机器同时访问一台访问,还没有把锁设置过去的时候,另一台也查不到就会出现这个问题

解决方法:这跟写代码方式有关。先查,如果不存在就set,这种方式有极微小的可能存在时间差,导致锁set了2次。

推荐使用 setIfAbsent 这样在 redis set 的时候是单线程的,不会存在重复问题

二、setIfAbsent的使用
1、作用:如果为空就set值,并返回1;如果存在(不为空)不进行操作,并返回0。

很明显,比get和set要好。因为先判断get,再set的用法,有可能重复set值。

2、setIfAbsent 和 setnx

setIfAbsent 是java中的方法

setnxredis命令中的方法

redis> SETNX mykey "Hello"  //存在mykey,设置值并返回1
(integer) 1
redis> SETNX mykey "World"  // 存在mykey,不处理并返回0
(integer) 0
redis> GET mykey
"Hello"
BoundValueOperations boundValueOperations = this.redisTemplate.boundValueOps(redisKey);
flag = boundValueOperations.setIfAbsent(value); // flag 表示是否set
boundValueOperations.expire(seconds, TimeUnit.SECONDS);
if(!flag){ // 重复
    repeatSerial.add(serialNo);
    continue;
}else{// 没有重复
    norepeatSerial.add(serialNo);
}

3、需要注意的是以前 stringRedisTemplate.setIfAbsent() 在服务器是由2个命令组成的 完成一个setnx时候设置 expire 时候中间中断了,无法保证原子性。

需要使用 4 个参数的那个重载方法,这个底层是 set key value [EX seconds] [PX milliseconds] [NX|XX] 是原子性的

public Boolean setIfAbsent(K key, V value, long timeout, TimeUnit unit) {
  byte[] rawKey = rawKey(key);
  byte[] rawValue = rawValue(value);
  Expiration expiration = Expiration.from(timeout, unit);
  return execute(connection -> connection.set(rawKey, rawValue, expiration, SetOption.ifAbsent()), true);
} 

三、如何使用呢?分布式锁
1、应用场景比如我们有2台服务器,4个镜像节点,那么就会有4个负载均衡后台服务,如果你需要代码加一定时任务。那么最后4个后台服务都会执行这个定时任务,就会重复4次。

2、怎么办呢?加分布式锁,就会用到上面的方法咯。

3、分布式锁实现可能遇到的问题:看这篇文章,关于stringRedisTemplate.setIfAbsent()并设置过期时间遇到的问题:https://blog.csdn.net/weixin_34419326/article/details/88677793,主要注意一下几点:

(1)加了事务管理之后,setIfAbsent的返回值null
在这里插入图片描述
(2)当出现并发时,String result = stringRedisTemplate.opsForValue().get(key); 这里就会有多个线程同时拿到为空的key,然后同时写入数据

(3)最终解决方法:将redis版本升级到2.1以上,然后使用直接在setIfAbsent中设置过期时间

在这里插入图片描述

原文地址:https://blog.csdn.net/weixin_45979191/article/details/131505819

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_33266.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注