复习
- Redis 数据类型 命令
- string
- list
- hash
- set
- sorted set
- redis使用规范
- key设计 命名规范 ugc:user:1
- value设计 避免BigKey
- string长度 大小
- …
- 禁用命令
- 怎么禁用 /etc/redis.conf rename-command flushdb “”
- 禁用 时间复杂度 O(n) keys / flushdb / flushall /config
- hgetall 谨慎使用
- keys hgetall smembers => scan / hscan /zscan /sscan
- scan 游标 match 模式 count 最大返回数量
- Java连接Redis
- jedis 将所有命令转换成方法
- spring-data-redis
- RedisTemplate
- opsForXXX
- boundXXXOps
- RedisTemplate
Redis特性
缓存
@GetMapping("/student/{id}")
public Student find(@PathVariable("id")Integer id) throws ParseException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
log.info("查找学生 :{}",id);
if(id==null){
return null;
}
Student student = null;
//1. 从缓存中获取
String stuKey= "stu:"+id;
if(redisTemplate.hasKey(stuKey)){
log.info("缓存中存在{},从缓存中获取",stuKey);
BoundHashOperations ops = redisTemplate.boundHashOps(stuKey);
//hash的所有字段,表示学生的所有信息
Map<String,Object> entries = ops.entries();
log.info("{}",entries);
// //转换成student对象 反序列化
student = new Student();
student.setClassName((String) entries.get("className"));
student.setStudentId(Integer.parseInt((String)entries.get("studentId")) );
student.setStudentName((String) entries.get("studentName"));
student.setStudentNo((String) entries.get("studentNo"));
student.setGender((String) entries.get("gender"));
student.setAge(Integer.parseInt((String) entries.get("age")));
student.setMajor((String) entries.get("major"));
//
String registerTime = (String) entries.get(entries.get("registerTime"));
// student.setRegisterTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(registerTime));
student.setCounselor((String) entries.get("counselor"));
// student = BeanUtils.mapToBean(entries, Student.class);
// student = new Student();
// BeanUtils.populate(student,entries);
return student;
}
//2. 从数据库中查询
log.info("查询数据库,id=",id);
student = studentService.findById(id);
//3. 加入到缓存中
log.info("存入缓存中{}",student);
BoundHashOperations ops = redisTemplate.boundHashOps(stuKey);
Map<String, String> stringObjectMap = BeanUtils.describe(student);
ops.putAll(stringObjectMap);
redisTemplate.expire(stuKey,30, TimeUnit.MINUTES);
return student;
}
- 不够通用 2. 不够简洁
Spring 缓存抽象 spring cache abstraction
增 @Cachable
删 @CacheEvict
改 @CachePut
查
在application类上加 @EnableCaching
@GetMapping("findById")
@Cacheable(cacheNames = "stuCache",key="#stuId")
public Student findById(int stuId){
log.info("从数据库获取数据{}",stuId);
return studentService.findById(stuId);
}
@PutMapping("updateStu")
@CachePut(cacheNames = "stuCache",key = "#student.studentId")
public Student updateStudent(@RequestBody Student student){
studentService.update(student);
return student;
}
@DeleteMapping("/deleteById")
@CacheEvict(cacheNames = "stuCache",key = "#id")
public void deleteStudent(int id){
studentService.delById(id);
}
- pom.xml必须要有redis的依赖, SpringCache 支持多种不同的缓存,其中包括内存缓存,如果没有redis依赖,则使用内存缓存
- 缓存对象,要求加Serializable接口
- 如何查看redis中的缓存?
keys *
string缓存配置
spring:
redis:
host: localhost
port: 6379
cache:
type: redis
redis:
time-to-live: 60M
key-prefix: 'redisbootdemo::'
缓存问题
缓存过期的作用=> 保证数据的一致性;减少内存空间;
缓存击穿:有一个key过期时,大量的查询该key的请求,透过redis进入到数据库,key是热点key
- 时间点:在某个key过期 到 该key的缓存重新生成之间
- 解决方案:在缓存过期后,请求加互斥锁
缓存穿透:大量查询不存在的key
- 解决方案:缓存空值;布隆过滤器 BloomFilter(超大的位数组,每1位只有0和1可以表示是否存在)
缓存雪崩:多个key在同一个时间点过期,造成大量的查询透过redis进入到数据库
- 解决方案:设置不同的过期时间
redis缓存支持
指定缓存的内存空间大小 maxmemory 100mb
达到内存上限时如何删除? maxmemory-policy
- noeviction:返回错误,当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外)
- allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
- volatile-lru: 尝试回收最少使用的键(LRU),但仅限于带有过期时间的键,使得新添加的数据有空间存放。
- allkeys-random: 回收随机的键使得新添加的数据有空间存放。
- volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
- volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。
LRU Least-Recently-Used 最近最少使用