springboot连接redis
1. 通过jedis类
- 先引依赖
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
- 使用
package com.daiji.test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import java.util.Set;
/**
* @Author 呆鸡
* @Date 2021/4/14 16:49
* @Version 1.0
*/
public class TestJedis {
public static void main(String[] args) {
Jedis jedis=new Jedis("192.168.31.52",6379);//必须运行远程连接 必须防火墙放行该端口号
//关于字符串
jedis.set("k1","v1");
jedis.set("k8","18");
jedis.mset("k2","v2","k3","v3","k4","v4");
jedis.setnx("k1","12");
jedis.decr("k8");
//操作key
Set<String> keys = jedis.keys("*");
System.out.println("所有的key:"+keys);
jedis.del("k1");
jedis.expire("k2",60);
jedis.ttl("k2");
}
}
- 使用jedis连接池
package com.ykq.test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @Author 闫克起
* @Date 2021/4/14 16:59
* @Version 1.0
*/
public class TestPoolJedis {
public static void main(String[] args) {
//连接池的配置
JedisPoolConfig config=new JedisPoolConfig();
config.setMaxTotal(100);//设置连接池的最大连接数
config.setMaxIdle(10);//设置最大空闲的个数
config.setTestOnBorrow(true);//在从连接池这种获取连接对象前是否测试该对象可以。
//创建连接池对象
JedisPool jedisPool=new JedisPool(config,"192.168.213.188",6379);
//获取jedis对象
Jedis jedis = jedisPool.getResource();
System.out.println(jedis.get("k3"));
jedis.close();//释放资源
}
}
2. 使用工具类RedisTemplate
- 引入依赖
<!---->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 配置文件
//连接单机redis
spring.redis.host=192.168.213.188 //ip
spring.redis.port=6379 //端口号
//连接哨兵集群
spring.redis.sentinel.master=mymaster //主机的名字
spring.redis.sentinel.nodes=192.168.213.188:26379 //主机id和端口号
//去中心化集群
spring.redis.cluster.nodes=192.168.213.188:8001,192.168.213.188:8002,192.168.213.188:8003,192.168.213.188:8004,192.168.213.188:8005,192.168.213.188:8006
- 使用redisTemplate该类可以存放任意类型的数据,但是该类型的数据必须实现序列,获取redis中对应的数据时,会进行反序列化。 如果使用RedisTemplate建议大家指定key,value,以及hashkey的序列化方式。
可以通过配置文件来指定序列化方式
package com.daiji.config;
/**
* @Author 呆鸡
* @Date 2021/4/15 16:16
* @Version 1.0
*/
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
import java.time.Duration;
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
//比如验证码
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600)) //缓存过期10分钟 ---- 业务需求。
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))//设置key的序列化方式
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) //设置value的序列化
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
3. 使用场景
1. 作为缓存
用redis做缓存:根据redis的get方法,如果有数据就是有缓存,如果没有数据就代表缓存中没有,就去访问数据库
可以使用注解来让复杂的代码简单化。
- 不试用注解
@Service
public class DeptService {
@Resource
private DeptDao deptDao;
@Autowired
private RedisTemplate redisTemplate;
public Dept findById(Integer deptId){
//1.从缓存中查询该数据
Object o = redisTemplate.opsForValue().get("findById::" + deptId);
if(o!=null){//表示从缓存中获取该数据
return (Dept) o;
}
Dept dept = deptDao.selectById(deptId);
redisTemplate.opsForValue().set("findById::"+deptId,dept);//把查询的结果放入缓存
return dept;
}
//数据库和缓存同步问题!
public int delete(Integer deptId){
redisTemplate.delete("findById::"+deptId);//删除缓存
int i = deptDao.deleteById(deptId);
return i;
}
public int update(Dept dept){
redisTemplate.delete("findById::"+dept.getDeptId());//删除缓存
int i = deptDao.updateById(dept);
redisTemplate.opsForValue().set("findById::"+dept.getDeptId(),dept);
return i;
}
}
- 使用注解(在启动类加上@EnableCaching注解来开启缓存注解)
@Service
public class DeptService {
@Resource
private DeptDao deptDao;
//该注解作用:会先查询缓存,如果缓存存在,则不会执行代码块。 如果缓存中不存在则执行该方法,并把该方法的返回值存放到redis中
@Cacheable(cacheNames = "findById",key = "#deptId") //缓存的key值 为findById
public Dept findById(Integer deptId){
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
Dept dept = deptDao.selectById(deptId);
return dept;
}
//数据库和缓存同步问题!
// beforeInvocation:是否在方法执行前就清空,缺省为 false,
// 如果指定为 true,则在方法还没有执行的时候就清空缓存。缺省情况下,如果方法执行抛出异常,则不会清空缓存。
@CacheEvict(cacheNames = "findById",key = "#deptId")
public int delete(Integer deptId){
int i = deptDao.deleteById(deptId);
return i;
}
//这个注解是必须执行方法体,而且会把方法体执行的结果放入到缓存中。 如果发生异常则不操作缓存。
@CachePut(cacheNames = "findById",key = "#dept.deptId")
public Dept update(Dept dept){
int i = deptDao.updateById(dept);
return dept;
}
}
2. 作为分布式锁
在分布式项目中,需要保证线程的安全,此时使用synchronized就不行了,因为在分布式项目中没办法使用同一个锁对象。
这时就可以通过使用redis数据库中的setnx方法,因为redis是单线程的,一次只能处理一个线程,所以如果返回true就代表你到了锁资源,如果返回false就代表没拿到锁资源。
使用完之后要删除该数据,以便后续的线程能通过setnx方法返回true
代码
@Service
public class StockService {
@Resource
private StockDao stockDao;
@Autowired
private StringRedisTemplate redisTemplate;
public String decrStock(Integer productId) {//synchronized () 同步方法 同步代码块
Boolean flag = redisTemplate.opsForValue().setIfAbsent("product::" + productId, "ykq",30, TimeUnit.SECONDS);
//查询对应的id的库存
if(flag) {//获取锁了
try {
Stock stock = stockDao.selectById(productId);
if (stock.getNum() > 0) {
//根据id修改库存
stock.setNum(stock.getNum() - 1);
stockDao.updateById(stock); //异常发生
// int c=10/0;
System.out.println("库存剩余:" + (stock.getNum()));
return "库存减少成功";
} else {
return "库存不足";
}
}catch (Exception e){
throw new RuntimeException(e.getMessage());
}
finally {
redisTemplate.delete("product::" + productId);//释放锁资源 一定再finally
}
}else{
System.out.println("服务器正忙请稍后再试..........");
return "服务器正忙请稍后再试..........";
}
}
}
或者使用第三方插件redisson:一个专门用来解决分布式问题的插件
- 引依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.4</version>
</dependency>
- 在配置文件中配置redis的配置
@Bean
public RedissonClient getRedisson(){
Config config=new Config();
config.useSingleServer().setAddress("redis://192.168.213.188:6379");
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
- 使用local或者trylock
@Service
public class StockService {
@Resource
private StockDao stockDao;
@Autowired
private RedissonClient redisson;
@Override
@CachePut(cacheNames = "find",key = "#id")
public Lock update(Integer id) {
RLock lock = redissonClient.getLock("update::" + id);
if (lock.tryLock()){
try {
Lock myLock = lockDao.selectById(id);
if (myLock!=null && myLock.getLockNum()>0){
myLock.setLockNum(myLock.getLockNum()-1);
lockDao.updateById(myLock);
System.out.println("票还剩"+myLock.getLockNum()+"张");
return myLock;
}
}finally{
if (lock.isLocked()){
lock.unlock();
}
}
}
return update(id);
}
}