在Java中操作Redis,使用Redis实现分布式锁


 

操作redis常用的有2种方式:spring data redis、jedis,jedis中的方法和redis-cli的操作方式相同,方法使用简单、方便,但需要自己创建连接,可以作为spring data redis之外一种的选择。
 

springboot整合redis(spring data方式)

依赖

创建时勾线redis
在这里插入图片描述
或者手动添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

 

application.properties

###redis config###
#单机版redis server
spring.redis.host=192.168.1.9
spring.redis.port=6379
#redis集群
#spring.redis.cluster.nodes=192.168.1.1:6379 192.168.1.2:6379 192.168.1.3:6379 192.168.1.4:6379 192.168.1.5:6379 192.168.1.6:6379

#要使用的数据库,0-15,缺省默认使用db0
#spring.redis.database=0
#连接到redis服务器的超时时间,ms
spring.redis.timeout=3000

#如果jedis连接池没有闲置连接可用,最多等待多少ms
spring.redis.jedis.pool.max-wait=3000
#默认8,-1表示不限制
spring.redis.jedis.pool.max-active=200
#默认0
spring.redis.jedis.pool.min-idle=50
#默认8
spring.redis.jedis.pool.max-idle=100

连接池默认使用jedis的

 

工具类

直接使用提供的方法操作redis有点麻烦,一句代码要写很长,可以自己封装一下

@Component
public 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);
    }

    //......

}

使用时注入RedisUtil,当然不封装,直接注入StringRedisTemplate也可以。

 

jedis方式

依赖

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.3.0</version>
</dependency>

 

使用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<Tuple> 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<HostAndPort> 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();

 

使用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


// jedis
jedis.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)    

// 删除key
redisTemplate.delete(key)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章