发布订阅
- 这个跟我们之前使用消息队列技术一样。
主要有两个关键操作:
消费者:订阅渠道subscribe channel1
生产者:发布消息到渠道 publish channel1 “redis is good”
内存管理–超时指令
Reids设置超时
- Redis 基于内存 ,而内存对于一个系统是最为宝贵的资源,而且它远远没有磁盘那么大,所以对于 Redis 的键值对的内存回收也是一个十分重要的问题,如果操作不当会产生 Redis岩机的问题,使得系统性能低下。
热数据:经常被查询的放到Redis中,
冷数据: 偶尔访问一次的放到磁盘的数据库中 - 数据销毁的方式有两种
1,del的方式,直接删除掉指定的key
2,设置key的超时时间 - 超时命令:
命令 | 作用 |
---|
expire key seconds | 设置超时时间戳,单位为秒 |
ttl key | 查看超时时间,-1表示没有超时时间,-2表示已经超时 |
persist key | 持久化key,取消超时时间 |
- 关键问题:
如果key超时了,Redis会回收key的存储空间吗?
答案是不会。Redis的key超时了,不会被自动回收,只会标识哪些键值对超时了。
Redis这么设计的原因是在于避免自动频繁回收,而造成的系统卡顿的情况。避免出现太多的内存碎片
Spring设置超时
@Test
public void testKeyTimeout2() throws Exception{
// 设置序列化方式方便客户端查看
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.opsForValue().set("k1","v1");
//redisTemplate.opsForValue().set("k1","v1",60,TimeUnit.SECONDS); // 创建key的时候设置超时时间
// 查看k1还有多久过期
redisTemplate.expire("k1",60,TimeUnit.SECONDS);
Thread.sleep(2000);
System.out.println(redisTemplate.getExpire("k1"));
System.out.println("-------------------------");
Thread.sleep(2000);
System.out.println(redisTemplate.getExpire("k1"));
}
Redis做为缓存服务的使用
查询
- 查询的时候先到Redis缓存中查询,如果有就返回,如果没有就到数据库中查询,查询完后需要缓存重建。
public List<User> getUserList() {
List<User> userList = null;
// 先到数据库中查询
userList = (List<User>) redisTemplate.opsForValue().get("userList");
if (userList == null) {
// 从数据库中查询
userList = userDao.select(null);
// 缓存重建
redisTemplate.opsForValue().set("userList", userList);
// 设置缓存失效时间
redisTemplate.expire("userList", 5, TimeUnit.MINUTES); // 5s后失效
}
return userList;
}
删除
public int addUser(User user) {
int i = userDao.insertSelective(user);
redisTemplate.delete("userList"); // 更新完后删除缓存数据
return i;
}
Redis锁处理
单体架构锁处理
@Override
public List<User> getUserList() {
List<User> userList = null;
userList= (List<User>) redisTemplate.opsForValue().get("userList");
if(userList == null){
synchronized (this){
if(userList == null){
System.out.println("查询数据库");
userList = userDao.select(null);
// 进行缓存重建
redisTemplate.opsForValue().set("userList",userList);
redisTemplate.expire("userList",5, TimeUnit.SECONDS); // 5s后失效
}
}
}
return userList;
}
分布式锁处理
- 利用setnx机制实现分布式锁处理,setnx机制的:在指定的 key 不存在时,为 key 设置指定的值,如果存在就不设置。
@PostConstruct // 在当前bean初始化完成后(redisTemplate已经注入成功)调用该方法
public void init(){
redisConnection = redisTemplate.getConnectionFactory().getConnection();
}
@Override
public List<User> getUserList() {
List<User> userList = null;
userList= (List<User>) redisTemplate.opsForValue().get("userList");
if(userList == null){
// 如果是true说明当前服务器已经拿到分布式锁
Boolean look = redisConnection.setNX("lock".getBytes(), "true".getBytes());
redisConnection.expire("lock".getBytes(),60); // 设置锁的超时时间为60s,60s后自动释放分布式锁
if(look){
if(userList == null){
System.out.println("查询数据库");
userList = userDao.select(null);
// 进行缓存重建
redisTemplate.opsForValue().set("userList",userList);
redisTemplate.expire("userList",5, TimeUnit.MINUTES); // 5s后失效
// 缓存完后要释放分布式锁,要不然永远就进行不了缓存重建
redisConnection.del("lock".getBytes());
}
}else{
// 如果没有拿到分布式锁就等待50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
return getUserList();
}
}
return userList;
}
- 没有拿到分布式锁的线程要等待一段时间后再次调用查询方法
- 拿到分布式锁在进行缓存重建后要对锁进行释放
- 设置分布式锁的时候要设置超时时间,避免刚拿到锁就出现故障的问题
SpringBoot注解实现缓存服务器
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
启动添加开启缓存注解
@SpringBootApplication(scanBasePackages = "com.qf")
@MapperScan(basePackages = "com.qf.dao")
@EnableCaching // 开启缓存注解
public class UserRedisApplication {
public static void main(String[] args) {
SpringApplication.run(UserRedisApplication.class, args);
}
自定义缓存过期时间
#缓存的类型是redis
spring.cache.type=redis
#缓存的过期时间是60s,单位是毫秒
spring.cache.redis.time-to-live=60000
通过注解操作缓存
- 注解
@Cacheable:用该注解修饰的方法表示,进入方法之前先查询缓存,最后把查询的结果放入到缓存中。
Value:缓存名称中的前缀
Key:缓存名称的后缀,需要用单引号引起来,在这个属性中可以通过#获取方法的返回值
#result.id – result代表方法的对象
#id – id代表方法的形参
@CacheEvict:删除指定的缓存
@CachePut:将方法的返回值添加到缓存中
@Caching:可以设置多个相同的缓存注解
具体操作
@Cacheable(value = "user",key = "'List'") // 查询数据库之前先到缓存中查询,key需要加引号
public List<User> getUserList() {
System.out.println("查询数据库");
return userDao.select(null);
}
@CacheEvict(value = "user",key = "'List'") // 添加之前删除缓存
@CachePut(value = "user",key = "'id'+#result.id") // 添加成功后添加单个的对象的缓存
public User addUser(User user) {
userDao.insertSelective(user);
return user;
}
@Override
@Caching(evict = {
@CacheEvict(value = "user",key = "'id'+#user.id"), // 删除单个对象缓存
@CacheEvict(value = "user",key = "'List'") // 删除列表缓存
})
public int updateUser(User user) {
return userDao.updateByPrimaryKey(user);
}
@Cacheable(value = "user",key = "'id'+#id")
public User getUserById(Integer id) {
System.out.println("查询了user:"+id);
return userDao.selectByPrimaryKey(id);
}
//Caching执行多个相同的缓存
@Caching(evict = {
@CacheEvict(value = "user",key = "'id'+#id"), // 删除单个对象缓存
@CacheEvict(value = "user",key = "'List'") // 删除列表缓存
})
public int deleteUser(Integer id) {
return userDao.deleteByPrimaryKey(id);
}