博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在Java中操作Redis,使用Redis实现分布式锁
阅读量:2421 次
发布时间:2019-05-10

本文共 5115 字,大约阅读时间需要 17 分钟。

目录

 

操作redis有2种常见方式

  • jedis:提供的方法和redis指令相同,方法使用简单、方便,但需要自己创建连接。
  • spring data redis:在jedis的基础上进行了更上层的封装,搭配spring使用十分方便。

 

jedis方式

依赖

redis.clients
jedis
3.3.0

 

使用jedis

// 重载方法很多,可指定超时时间、是否使用ssl等Jedis jedis=new Jedis("192.168.1.9", 6379);// jedis.auth("xxxx");  //如果需要密码// jedis.select(0);  //选择要使用的数据库if (jedis.ping().equals("PONG")){
//连接成功 jedis.zadd("users", 1223, "zhangsan"); jedis.zadd("users", 2568, "lisi"); jedis.zadd("users", 1379, "wangwu"); //迭代 Set
users = jedis.zrangeWithScores("users", 0, -1); //[0,-1] for (Tuple user:users){
String name = user.getElement(); //获取元素值 double score = user.getScore(); //获取分数 System.out.println(name+"的积分是:"+score); }}else{
System.out.println("无法连接redis服务器");}jedis.close();

 

从jedis连接池获取连接

// jedis连接池配置JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();jedisPoolConfig.setMaxTotal(15);jedisPoolConfig.setMaxIdle(10);jedisPoolConfig.setMinIdle(5);jedisPoolConfig.setMaxWaitMillis(180);//若一个Jedis对象闲置多少秒,就销毁// 重载方法很多,可指定超时时间、是否使用ssl、密码等JedisPool jedisPool= new JedisPool(jedisPoolConfig, "192.168.1.9", 6379);Jedis jedis = jedisPool.getResource();

 

连接redis集群

// 集群节点Set
nodes = new HashSet();nodes.add(new HostAndPort("192.168.1.1", 6379));nodes.add(new HostAndPort("192.168.1.2", 6379));nodes.add(new HostAndPort("192.168.1.3", 6379));nodes.add(new HostAndPort("192.168.1.4", 6379));nodes.add(new HostAndPort("192.168.1.5", 6379));nodes.add(new HostAndPort("192.168.1.6", 6379));// 获取JedisCluster对象,通过JedisCluster对象来操作JedisCluster jedisCluster = new JedisCluster(nodes);String user = jedisCluster.get("user");jedisCluster.close();

 

springboot整合redis(spring data方式)

依赖

创建时勾线redis

在这里插入图片描述
或者手动添加依赖

org.springframework.boot
spring-boot-starter-data-redis

 

yml

spring:  redis:    #单机版redis配置,默认localhost、6379、0    host: 192.168.1.1    port: 6379    database: 0    #连接到redis服务器的超时时间,ms    timeout: 3000    #如果是redis集群,则使用以下配置#    cluster:#      nodes: 192.168.1.1:6379,192.168.1.2:6379,192.168.1.3:6379    #jedis连接池配置    jedis:      pool:        #默认8,-1表示不限制        max-active: 200        #默认0        min-idle: 50        #默认8        max-idle: 100        #如果jedis连接池没有闲置连接可用,最多等待多少ms        max-wait: 3000

 

工具类

直接使用RedisTemplate提供的方法,一句代码要写很长,可以自己封装一下,使用时注入RedisUtil;也可以不封装,直接使用RedisTemplate。

@Componentpublic class RedisUtil {
@Autowired private static StringRedisTemplate redisTemplate; public static void set(String key,String value){
redisTemplate.opsForValue().set(key,value); } public static String get(String key){
return redisTemplate.opsForValue().get(key); } public static void hset(String key,String field,Object value {
redisTemplate.opsForHash().put(key,field,value); } public static Object hget(String key,String field){
return redisTemplate.opsForHash().get(key,field); } //......}

 

使用Redis实现分布式锁

分布式锁需要解决的问题

  • 互斥性:同一时间段,只能有一个redis客户端获取到锁、持有锁
  • 安全性:锁只能由持有者删除,不能由其它redis客户端删除
  • 防止死锁:若持有锁的redis客户端长时间未完成任务,可能发生死锁,应该及时释放锁
  • 容错:持有锁的redis客户端故障时,应该及时释放锁,避免锁一直被持有

 

jedis方式

setnx() 设置key

  • 若返回1,说明设置成功,获取到锁;获取到锁后用expire() 给key设置有效期,预防发生故障锁不能及时释放
  • 若返回0,说明设置失败,这个key已经存在,锁已被其它线程持有,重试。重试可以用for循环来写,设置重试次数,失败时线程沉睡一小段时间,醒来继续循环、重试,如果达到指定次数还未获取到锁就退出循环、放弃

在任务执行过程中,如果发生异常,不能继续往下执行,也应该马上释放锁。任务完成后用del()删除key或者用expire()将有效期置为0,释放锁。

 

上面的方式存在问题:分为setnx()、expire()2步,原子性得不到满足,可能出现并发问题。

更好的方式:使用redis命令的原子性

#ex指定过期时间(s),px也是指定过期时间(ms),nx是key不存在时才执行set(即新建),xx表示key已存在时才执行set(即更新)set lock "xxxxxxx" ex 60 nx// jedisjedis.set("lock", "xxxxxx", SetParams.setParams().ex(120).nx());

值一般设置为线程号

 

spring data redis方式

对应的方法如下

// 设置key,absent,缺席、不存在,返回布尔值Boolean redisTemplate.opsForValue().setIfAbsent(key,value)     // 设置有效期,key、数值、单位redisTemplate.expire(key,2,TimeUnit.MINUTES)// 更好的写法,原子性Boolean redisTemplate.opsForValue().setIfAbsent(key,value,2,TimeUnit.MINUTES)    // 删除keyredisTemplate.delete(key)

 

redis的pipeline交互模型

 

在这里插入图片描述
 

pipeline 管道、流水线。

如果客户端短时间内需要执行多个redis命令,且后面的命令不依赖前面命令的执行结果,则可以使用pipeline打包发送,以减少网络通信的时间开销,解决频繁的命令往返造成的性能瓶颈。

@Autowiredprivate StringRedisTemplate redisTemplate;public void test() {
//... //返回值list,元素对应各条命令的执行结果 List resultList = redisTemplate.executePipelined(new RedisCallback() {
@Override public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
//打开一个pipeline redisConnection.openPipeline(); redisConnection.set("name".getBytes(), "chy".getBytes()); redisConnection.set("tel".getBytes(), "188xxxxxx".getBytes()); redisConnection.set("email".getBytes(), "xxxxxxx@qq.com".getBytes()); //不要调用redisConnection的closePipeline()关闭pipeline,也不要关闭连接欸,不然拿不到返回的执行结果 //返回null即可 return null; } }); //...}
  • 不要一次性组装发送太多的命令,或者设置big key这种数据量很大的key,容易出现网络拥塞。
  • redis提供的mset、mget之类的原生的操作key、操作数据类型的命令都是原子性的,但redis的事务、pipeline都只是将多条命令打包发送给redis服务器,这多条命令在redis服务器上的执行并不具备原子性,某条命令执行失败时,都是跳过失败的命令继续执行后续命令,不会回滚之前的操作。
  • redis的事务具有隔离性,redis服务器执行事务时,中间不会穿插执行其它客户端的命令;执行pipeline中的命令时,中间可能会穿插执行其它客户端的命令。

转载地址:http://bzhlb.baihongyu.com/

你可能感兴趣的文章
二线城市容不下程序员
查看>>
不要成为自己讨厌的那种程序员 | 程序员有话说
查看>>
为什么程序员下班后只关显示器从不关电脑?
查看>>
滴滴裁员 2000 人,具体补偿方案已出
查看>>
余生,做个不焦虑的程序员!
查看>>
世界排名第 3 的滴滴裁员,开春求职必知的独角兽排行榜
查看>>
Spring Boot 中的响应式编程和 WebFlux 入门
查看>>
阿里终结裁员危机!坚决不拿 10 万阿里人祭天!
查看>>
如何从零开始两天撸一个微信小程序?!(内含源码)
查看>>
女神?御姐?文艺?这样的程序媛你绝没见过! | 程序员有话说
查看>>
“软件外包城”下的马鞍山 | 程序员有话说
查看>>
那些上相亲网站的程序员,后来怎么样了?
查看>>
程序员如何实现财富自由?
查看>>
你我的父母,都在被互联网“割韭菜”
查看>>
程序员下班后都忙些啥?| 程序员有话说
查看>>
网易不再从容
查看>>
万万没想到你们竟是这样的程序员 | 程序员有话说
查看>>
Java 帝国对 Python 的渗透能成功吗?
查看>>
从培训机构出来的程序员,后来都怎么样了? | 程序员有话说
查看>>
程序员写代码没激情该怎么破?
查看>>