基于redis setifabsent的使用说明-kb88凯时官网登录

来自:网络
时间:2021-02-06
阅读:
免费资源网 - https://freexyz.cn/

如果为空就set值,并返回1

如果存在(不为空)不进行操作,并返回0

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

setifabsent 和 setnx

setifabsent 是java中的方法

setnx 是 redis命令中的方法

setnx 例子

redis> setnx mykey "hello"
(integer) 1
redis> setnx mykey "world"
(integer) 0
redis> get mykey
"hello"

setifabsent 例子

代码:

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);
}

补充:使用redis事物解决stringredistemplate.setifabsent()并设置过期时间遇到的问题

spring-date-redis版本:1.6.2

场景:

在使用setifabsent(key,value)时,想对key设置一个过期时间,同时需要用到setifabsent的返回值来指定之后的流程,所以使用了以下代码:

boolean store = stringredistemplate.opsforvalue().setifabsent(key,value);
if(store){
 stringredistemplate.expire(key,timeout); 
 // todo something... 
}

这段代码是有问题的:当setifabsent成功之后断开连接,下面设置过期时间的代码stringredistemplate.expire(key,timeout); 是无法执行的,这时候就会有大量没有过期时间的数据存在数据库。想到一个办法就是添加事务管理,修改后的代码如下:

stringredistemplate.setenabletransactionsupport(true);
stringredistemplate.multi();
boolean store = stringredistemplate.opsforvalue().setifabsent(key,value);
if(store){
 stringredistemplate.expire(key,timeout);  
}
stringredistemplate.exec();
if(store){
  // todo something...
}

这样就保证了整个流程的一致性。本因为这样就可以了,可是事实总是不尽人意,因为我在文档中发现了以下内容:

加了事务管理之后,setifabsent的返回值竟然是null,这样就没办法再进行之后的判断了。

好吧,继续解决:

stringredistemplate.setenabletransactionsupport(true);
stringredistemplate.multi();
string result = stringredistemplate.opsforvalue().get(key);
if(stringutils.isnotblank(result)){
  return false;
}
// 锁的过期时间为1小时
stringredistemplate.opsforvalue().set(key, value,timeout);
stringredistemplate.exec();
// todo something...

上边的代码其实还是有问题的,当出现并发时,string result = stringredistemplate.opsforvalue().get(key); 这里就会有多个线程同时拿到为空的key,然后同时写入脏数据。

最终解决方法:

使用stringredistemplate.exec();的返回值判断setifabsent是否成功

stringredistemplate.setenabletransactionsupport(true);
stringredistemplate.multi();
stringredistemplate.opsforvalue().setifabsent(lockkey,json.tojsonstring(event));
stringredistemplate.expire(lockkey,constants.redis_key_expire_second_1_hour, timeunit.seconds);
list result = stringredistemplate.exec(); // 这里result会返回事务内每一个操作的结果,如果setifabsent操作失败后,result[0]会为false。
if(true == result[0]){
 // todo something...
}

将redis版本升级到2.1以上,然后使用

直接在setifabsent中设置过期时间

update :

java 使用redis的事务时不能直接用api中的multi()和exec(),这样multi()和exec()两次使用的stringredistemplate不是一个connect,会导致死锁,正确方式如下:

private boolean setlock(recordeventmodel event) {
    string lockkey = event.getmodel()   ":"   event.getaction()   ":"   event.getid()   ":"   event.getmessage_id();
    log.info("lockkey : {}" , lockkey);
    sessioncallback sessioncallback = new sessioncallback() {
      list
网站地图