缓存机制 | 作用 | 使用频率 | 难易度 |
---|---|---|---|
OScache | JVM内置缓存, 用于缓存静态页面 | 低 | 简单 |
Ehcache | JVM内置缓存, 用于缓存数据库取到的数据 | 低 | 简单 |
自设HashMap | 自己coding,将HashMap用来将需要的数据存在JVM中供使用, 不过要有一个清理Map中无效缓存的机制,避免缓存内容越来越多造成OOM | 中 | 复杂 |
Redis | 独立的缓存软件, 通过KV, 可以存取任意数据, 自带无效数据清理机制和连接池设置 | 高 | 低 |
直接用docker命令: docker run -p 6379:6379 -d redis:latest redis-server
进入redis docker: docker exec -it redis redis-cli
Redis三大框架:
概念:
Jedis:是Redis的Java实现客户端,提供了比较全面的Redis命令的支持,
Redisson:实现了分布式和可扩展的Java数据结构。
Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。
优点:
Jedis:比较全面的提供了Redis的操作特性
Redisson:促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过Redis支持延迟队列
Lettuce:主要在一些分布式缓存框架上使用比较多
可伸缩:
Jedis:使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。(类似于mysql用connection直连)
Redisson:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Redisson的API是线程安全的,所以可以操作单个Redisson连接来完成各种操作
Lettuce:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作
(类似于mysql用mybatis连接)
Springboot从2.0.0开始, spring-boot-starter-data-redis默认使用lettuce框架.
(包中也包括jedis,但是没有使用)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
commons-pool2是为设置Redis连接池
#redis连接配置(必须)
spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=xxxxx(可以没有密码)
#(可选)
#最大空闲数
spring.redis.lettuce.pool.max-idle=80
##最小空闲数
spring.redis.lettuce.pool.min-idle=10
##最大连接数:能够同时建立的“最大链接个数”
spring.redis.lettuce.pool.max-active=500
##最大建立连接等待时间:单位ms
spring.redis.lettuce.pool.max-wait=1ms
##当客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能,单位毫秒
spring.redis.lettuce.shutdown-timeout=100ms
增加和修改是一种方法, 如果数据不存在就会增加, 存在就会修改
string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。
string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。
string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。
常用命令:set、get、decr、incr、mget等。
注意:一个键最大能存储512MB。
package com.example.demo.controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ZSetOperations;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import com.example.demo.model.Student;
@RestController
public class StudentController {
@Autowired
private RedisTemplate<String, String> redistemp;
//StringRedisTemplates是RedisTemplate的子类,等于RedisTemplate
//对字符串进行存取时,可以直接用RedisTemplate的对象, 新增和更新是同一个方法,如果没有key则新增,有则更新
@PostMapping("/setstring")
public void set(@RequestParam(value="key") String key,@RequestParam(value="value") String value){
System.out.println(key);
redistemp.opsForValue().set(key, value); //对象和字符串的存取都使用opsForValue()
}
//对key设置10秒的expire时间,10秒后key不可用,redis使用的过期键值删除策略是:
//惰性删除加上定期删除,两者配合使用。(失效后不会立即删除,保留到下次查询时删除,如果一直没有查询,则定期删除)
@PostMapping("/setstringwithtime")
public void setwithtime(@RequestParam(value="key") String key,@RequestParam(value="value") String value){
redistemp.opsForValue().set(key, value, 20, TimeUnit.SECONDS);
}
}
@GetMapping("/getstring/{key}")
public String get(@PathVariable("key") String key){
return redistemp.opsForValue().get(key);
}
package com.example.demo.model;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
@Data
public class Student implements Serializable {
/**
* 必须实现Serializable 以便序列化
*/
private static final long serialVersionUID = 1L;
/**
*
*/
private Integer id;
private String name;
private Double score;
private Date birthday;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getScore() {
return score;
}
public void setScore(Double score) {
this.score = score;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
package com.example.demo.dao.redis;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.StringRedisSerializer;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
@Configuration
public class RedisConfigration {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<Object,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate ;
}
}
//前置import代码同String存取
@RestController
public class StudentController {
@Resource
private RedisTemplate<String, Object> redisTemplate;
//对对象进行存取时,需要序列化和反序列化对象,配置在RedisConfigration中
@PostMapping("/setstudent")
public void setstudent(@RequestBody Student student){
redisTemplate.opsForValue().set(student.getName(), student);
}
@GetMapping("/getstudent/{key}")
public Student getstudent(@PathVariable("key") String key){
return (Student)redisTemplate.opsForValue().get(key);
}
//删除方法
@DeleteMapping("/deletestudent/{key}")
public boolean deletestudent(@PathVariable("key") String key){
redisTemplate.delete(key);
return redisTemplate.hasKey(key);
}
}
{
"id": 123,
"name" : "nongfu1",
"score" : 80.5,
"birthday" : "1986-04-30"
}
Redis hash 是一个键值(key=>value)对集合;是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
每个 hash 可以存储 232 -1 键值对(40多亿)。
常用命令:hget、hset、hgetall等。
应用场景:存储一些结构化的数据,比如用户的昵称、年龄、性别、积分等,存储一个用户信息对象数据。
//redis存取哈希(相当于java中的hashmap)(cli命令hgetall k1))
@PostMapping("/sethash")
public String sethash(){
HashOperations<String,String,String> hashopreation = redistemp.opsForHash();
hashopreation.put("k1", "hk1", "v1");
hashopreation.put("k1", "hk2", "v2");
hashopreation.put("k1", "hk3", "v3");
String hm = hashopreation.get("k1", "hk2");
return hm;
}
//接口调用返回结果是v2
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
list类型经常会被用于消息队列的服务,以完成多程序之间的消息交换。
常用命令:lpush、rpush、lpop、rpop、lrange等。
列表最多可存储 232 - 1 元素 (4294967295, 每个列表可存储40多亿)。
//redis存取列表(相当于java中的Arraylist)(cli命令lrange left 0 3)(双向可存取)
@PostMapping("/setlist")
public List<String> setlist(){
ListOperations<String, String> listoperation = redistemp.opsForList();
listoperation.leftPush("left", "v1");
listoperation.leftPush("left", "v2");
listoperation.leftPush("left", "v3");
List<String> list = listoperation.range("left", 0, 1);
return list;
}
//返回结果[v3,v2]
Redis的Set是string类型的无序集合。和列表一样,在执行插入和删除和判断是否存在某元素时,效率是很高的。集合最大的优势在于可以进行交集并集差集操作。Set可包含的最大元素数量是4294967295。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
应用场景:
1、利用交集求共同好友。
2、利用唯一性,可以统计访问网站的所有独立IP。
3、好友推荐的时候根据tag求交集,大于某个threshold(临界值的)就可以推荐。
常用命令:sadd、spop、smembers、sunion等。
集合中最大的成员数为 232 - 1(4294967295, 每个集合可存储40多亿个成员)。
//redis存取无序集合(相当于java中的HashSet)(cli命令smember set1)
@PostMapping("/setset")
public Set<String> setset(){
SetOperations<String, String> setoperation = redistemp.opsForSet();
setoperation.add("set1", "s1");
setoperation.add("set1", "s1");
setoperation.add("set1", "s2");
setoperation.add("set1", "s3");
Set<String> set = setoperation.members("set1");
return set;
}
//返回结果[s1,s2,s3]
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
sorted set是插入有序的,即自动排序。
常用命令:zadd、zrange、zrem、zcard等。
当你需要一个有序的并且不重复的集合列表时,那么可以选择sorted set数据结构。
应用举例:
(1)例如存储全班同学的成绩,其集合value可以是同学的学号,而score就可以是成绩。
(2)排行榜应用,根据得分列出topN的用户等。
//redis存取有序集合(相当于java中的TreeSet)(cli命令zrangebyscore setz 0 2))
@PostMapping("/setzset")
public Set<String> setzset(){
ZSetOperations<String, String> zsetoperation = redistemp.opsForZSet();
zsetoperation.add("setz", "s1", 1);
zsetoperation.add("setz", "s2", 4);
zsetoperation.add("setz", "s3", 3);
Set<String> zset = zsetoperation.rangeByScore("setz", 0, 5);
return zset;
}
//返回结果[s1,s3,s2]