学习目标:
1.java连接redis。
2.springboot整合redis。
3.springboot使用redis作为缓存。
4.redis常见的面试题。
1.java连接redis。
1.1 引入redis的依赖------>jedis
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.0</version>
</dependency>
</dependencies>
1.2 java代码操作redis
package com.test;
import redis.clients.jedis.Jedis;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;public class TestRedis {
public static void main(String[] args) {
//1.获取连接对象
Jedis jedis=new Jedis("192.168.223.128",6379); //默认连接的为localhost 端口号为6379
//2.操作redis数据库 -----String类型
jedis.set("k1","v1");
jedis.set("k2","2");
jedis.setnx("k1","v3");
jedis.mset("k3","v3","k4","v4");
List<String> mget = jedis.mget("k1", "k2");
System.out.println(mget);
jedis.flushAll();//3.操作hash类型
Map<String,String> map=new HashMap<>();
map.put("name","闫克起");
map.put("age","17");
map.put("sex","男");
jedis.hset("k1",map);Map<String, String> k1 = jedis.hgetAll("k1");
System.out.println(k1);Set<String> keys = jedis.keys("*");
System.out.println(keys);
}
}
2.springboot整合redis。
2.1 依赖---->启动依赖---->自动装配---->自动读取配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.2 代码测试--------StringRedistemplate
@Autowired
private StringRedisTemplate stringRedisTemplate;//存放的数据格式为string类型
@Test
void contextLoads() {
// //使用ValueOperations -----操作的为字符串类型
// ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
// ops.set("k1","springboot-v1");
// System.out.println(ops.get("k1"));
// Boolean aBoolean = ops.setIfAbsent("k2", "王沛他很听话");//setnx
// System.out.println(aBoolean);
// Map<String,String> map=new HashMap<>();
// map.put("name","张三");
// map.put("age","18");
// map.put("name","李四");
// ops.multiSet(map);
// System.out.println(stringRedisTemplate.keys("*"));
// //操作hash类型 HashOperations<String, Object, Object>
HashOperations<String, Object, Object> hash = stringRedisTemplate.opsForHash();
hash.put("k1","name","赵露露");
hash.put("k1","age","18");
Object o = hash.get("k1", "name");
System.out.println(hash.keys("k1"));
System.out.println(hash.values("k1"));
System.out.println(o);
System.out.println(hash.entries("k1"));
}
2.3 代码测试----------RedisTemplate
它的key和value可以存放Object类型,那么它的key和value默认采用的序列化方式为JDK序列化方式。一般我们需要为key和value指定序列化方式。
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
@Autowired
private RedisTemplate redisTemplate;@Test
void contextLoadsHash() {
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
HashOperations hash = redisTemplate.opsForHash();
hash.put("u","n","崔梦");
hash.put("u","a","25");
}
@Test
void contextLoads() {
//因为RedisTemplate默认会为key和value进行序列 按照JDK的序列化方式进行的,
//再实际开发过程中不用默认的序列化方式。空间少 而且不用为实体类实现序列化接口
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
ValueOperations valueOperations = redisTemplate.opsForValue();
//可以指定key的序列化方式
valueOperations.set("k4",new User("王五",25,"郑州"));
Object k4 = valueOperations.get("k4");
System.out.println(k4);
// Object k = valueOperations.get("k");
// System.out.println(k);}
3. 连接集群
哨兵
集群:
4. springboot使用redis作为缓存。
作用: 为了减少对数据库的访问频率。从而提高项目的性能。
什么样的数据适合放入缓存中。
1. 查询频率高
2. 修改频率低
3. 数据安全行要求低的
package com.yyh.redis1.sercice; import com.yyh.redis1.entity.User; import com.yyh.redis1.mapper.UserMapper; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; @Service public class UserService2 { @Resource private UserMapper userMapper; @Resource private RedisTemplate redisTemplate; public User selectId(Long id){ //1.序列化 redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setKeySerializer(new StringRedisSerializer()); //2.存放key value ValueOperations valueOperations = redisTemplate.opsForValue(); //3.先获取redis缓存里数据 Object o=valueOperations.get("user::selectid::"+id); //4.如果存在,把缓存里的数据返回 if(o!=null){ return (User) o; } //5.否则查询数据库 并把查询结果放入redis缓存里 然后把查询结果返回 User user= userMapper.selectById(id); if(user!=null){ valueOperations.set("user::selectid::"+id,user,30, TimeUnit.MINUTES); } return user; } public void delete(Long id){ redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setKeySerializer(new StringRedisSerializer()); ValueOperations valueOperations = redisTemplate.opsForValue(); //1.先删除redis里的数据 redisTemplate.delete("user::findById::"+id); //2.再删除数据库里的数据 userMapper.deleteById(id); } public void update(User user){ redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setKeySerializer(new StringRedisSerializer()); ValueOperations valueOperations = redisTemplate.opsForValue(); //1.先修改数据库里的数据 userMapper.updateById(user); //2.再修改缓存里的数据 valueOperations.set("user::findById::"+user.getId(),user); } public void insert(User user){ userMapper.insert(user); } }
缓存注解: 我只需要再相应方法上加入相应的缓存注解则可以完成缓存的功能
1.加入缓存的配置类
package com.yyh.redis1.config; 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.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.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; @Configuration public class MyConfig { //比如验证码 @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; } }
2.再相应的方法上加入缓存注解
package com.yyh.redis1.sercice; import com.yyh.redis1.entity.User; import com.yyh.redis1.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.GetMapping; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; @Service public class UserService { @Resource private UserMapper userMapper; @Resource private RedisTemplate redisTemplate; @Cacheable(cacheNames = "user::selectById",key="#id") public User selectById(Long id){ User user= userMapper.selectById(id); return user; } @CacheEvict(cacheNames = "user::delete",key = "#id") public void delete(Long id){ userMapper.deleteById(id); } @CachePut(cacheNames = "user::update",key = "#user.id") public User update(User user){ userMapper.updateById(user); return user; //2.再修改缓存里的数据 } public void insert(User user){ userMapper.insert(user); } }
3.启动缓存注解
5. redis缓存穿透
1. 缓存没有该数据并且数据库中也没有该数据, 这是如果有大量的恶意请求来访问这种数据时,这些请求穿过缓存,到达数据库。
1. 如果数据库中不存在该数据时,往缓存中存放一个空对象。该对象什么也不做。(缓存中会存在大量的冗余数据。设置很短的过期时间)。
2. 使用布隆过滤器。理解为一个容器
把数据库中的所有数据id放入到布隆过滤器中
6. 分布式锁
synchronized: 可以加载方法上,方法体内,类上。 自动获取锁,自动是否释放锁。 不会造成死锁。
lock: 方法体内。 手动获取锁 手动是否锁 如果出现异常而没有手动释放锁 则造成死锁。
使用redis中的setNx判断是否获取锁资源 使用del释放锁资源