Redis分布式锁,基于StringRedisTemplate和基于Lettuce实现setNx

使用redis分布式锁,来确保多个服务对共享数据操作的唯一性
一般来说有StringRedisTemplate和RedisTemplate两种redis操作模板。

根据key-value的类型决定使用哪种模板,如果k-v均是String类型,则使用StringRedisTemplate,否则使用RedisTemplate

redis加锁操作
必须遵循原子性操作,保证加锁的唯一性
核心方法
set(lockKey,value,"NXXX","EXPX",expireTime)
NXXX:只能取NX或者XX,NX-key不存在时进行保存,XX-key存在时才进行保存
EXPX:过期时间单位 (EX,PX),EX-秒,PX-毫秒

使用StringRedisTemplate实现加锁

1public class StringRedisTemplateImplClient { 2 // NX,XX 3 //NX-key不存在则保存,XX-key存在则保存 4 private static final String STNX= "NX"; 5 6 //EX,PX 7 //EX-秒,PX-毫秒 8 private static final String SET_EXPIRE_TIME = "PX"; 9 10 private RedisTemplate redisTemplate; 11 private StringRedisTemplate stringRedisTemplate; 12 13 14 public StringRedisTemplateImplClient(RedisTemplate redisTemplate){ 15 this.redisTemplate = redisTemplate; 16 this.stringRedisTemplate = new StringRedisTemplate(); 17 this.stringRedisTemplate.setConnectionFactory(redisTemplate.getConnectionFactory()); 18 this.stringRedisTemplate.afterPropertiesSet(); 19 } 20 21 public StringRedisTemplateImplClient(StringRedisTemplate redisTemplate){ 22 this.stringRedisTemplate = redisTemplate; 23 } 24 25 public boolean addRedisLock(String lockKey,String requestId,long expireTime){ 26 boolean result = stringRedisTemplate.opsForValue() 27 .setIfAbsent(lockKey,lockKey,expireTime, TimeUnit.SECONDS); 28 return result; 29 } 30}

下面简要分析下这个方法的一致性,查看setIfAbsent的源码

setIfAbsent这个方法是spring-data-redis提供的,分析其源码,实现类为org.springframework.data.redis.core.DefaultValueOperations
方法如下:
key-需要加锁的key
value-需要加锁的value
timeout-失效的时间
timeunit-常量,指定失效的时间单位

connection默认为lettuce驱动连接(springboot 2.0以后的版本)

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

其中SetOption为指定NX/XX类型

1public static enum SetOption { 2 UPSERT, 3 SET_IF_ABSENT, 4 SET_IF_PRESENT; 5 6 private SetOption() { 7 } 8 9 public static RedisStringCommands.SetOption upsert() { 10 return UPSERT; 11 } 12 13 public static RedisStringCommands.SetOption ifPresent() { 14 return SET_IF_PRESENT; 15 } 16 17 public static RedisStringCommands.SetOption ifAbsent() { 18 return SET_IF_ABSENT; 19 } 20}

查询spring官网查询这三个属性

SET_IF_ABSENT--->NX
SET_IF_PRESENT--->XX

自定义实现SetNx

setNx+expireTime实现加锁

 核心代码

1String status = stringRedisTemplate.execute(new RedisCallback<String>() { 2 @Override 3 public String doInRedis(RedisConnection connection) throws DataAccessException { 4 Object nativeConnection = connection.getNativeConnection(); 5 String status = null; 6 RedisSerializer<String> stringRedisSerializer = (RedisSerializer<String>) stringRedisTemplate.getKeySerializer(); 7 8 byte[] keyByte = stringRedisSerializer.serialize(key); 9 //springboot 2.0以上的spring-data-redis 包默认使用 lettuce连接包 10 11 //lettuce连接包,集群模式,ex为秒,px为毫秒 12 if (nativeConnection instanceof RedisAdvancedClusterAsyncCommands) { 13 logger.debug("lettuce Cluster:---setKey:"+setKey+"---value"+value+"---maxTimes:"+expireSeconds); 14 status = ((RedisAdvancedClusterAsyncCommands) nativeConnection) 15 .getStatefulConnection().sync() 16 .set(keyByte,keyByte,SetArgs.Builder.nx().ex(30)); 17 logger.debug("lettuce Cluster:---status:"+status); 18 } 19 //lettuce连接包,单机模式,ex为秒,px为毫秒 20 if (nativeConnection instanceof RedisAsyncCommands) { 21 logger.debug("lettuce single:---setKey:"+setKey+"---value"+value+"---maxTimes:"+expireSeconds); 22 status = ((RedisAsyncCommands ) nativeConnection) 23 .getStatefulConnection().sync() 24 .set(keyByte,keyByte, SetArgs.Builder.nx().ex(30)); 25 logger.debug("lettuce single:---status:"+status); 26 } 27 return status; 28 } 29}); 30logger.debug("getLock:---status:"+status);//执行正确status="OK"
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Nginx + lua +[memcached,redis]

精品案例1、Nginxluamemcached,redis实现网站灰度发布2、分库分表/基于Leaf组件实现的全球唯一ID(非UUID)3、Redis独立数据监控,实现订单超时操作/MQ死信操作SelectPollEpollReactor模型4、分布式任务调试Quartz应用