JAVA缓存机制和Redis配置使用

缓存机制 作用 使用频率 难易度
OScache JVM内置缓存, 用于缓存静态页面 简单
Ehcache JVM内置缓存, 用于缓存数据库取到的数据 简单
自设HashMap 自己coding,将HashMap用来将需要的数据存在JVM中供使用, 不过要有一个清理Map中无效缓存的机制,避免缓存内容越来越多造成OOM 复杂
Redis 独立的缓存软件, 通过KV, 可以存取任意数据, 自带无效数据清理机制和连接池设置

Redis配置使用

0. 运行Redis

直接用docker命令: docker run -p 6379:6379 -d redis:latest redis-server
进入redis docker: docker exec -it redis redis-cli

1. Maven插件

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连接池

2. Properties文件设置

#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

3. JAVA代码实现

3.1 Redis支持5种数据类型的增删改查

增加和修改是一种方法, 如果数据不存在就会增加, 存在就会修改

3.1.1 String(字符串) (包括对象)

string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。

string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。

string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。

常用命令:set、get、decr、incr、mget等。

注意:一个键最大能存储512MB。

3.1.1.1 字符串存取

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);
	}

3.1.1.2 对象存取

  1. 创建需要存取对象的Javabean
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;
	}


}

  1. 因为存取对象时需要序列化和反序列化, 需要创建Redis配置类, 重写redisTemplate方法, 设置可用的序列化方法(现在使用的是Jackson2JsonRedisSerializer), config类为固定代码, 使用时无需改动
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 ;
	}
  }
  1. Java代码实现对对象的存取

//前置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);
	}
	
}

  1. 使用postman调用接口, 格式使用raw-json(application/json)
{
	"id": 123,
	"name" : "nongfu1",
	"score" : 80.5,
	"birthday" : "1986-04-30"
}

3.1.2 Hash(哈希)

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

3.1.3 List(列表)

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]

3.1.4 Set(无序集合)

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]

3.1.5 (sorted set:有序集合)

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]

你可能感兴趣的:(IT)