为什么要使用缓存?
一般我们的网站开发完成,上线之后,服务器的读写效率是网站运行速度的重要条件,当然还有服务器的带宽等,但是这些东西都可以通过硬件的更新升级来解决。其实与网站运行效率息息相关的东西,就是我们的------数据库。数据库处理数据的速度,与网站速度息息相关,而数据查询、数据处理等等,都和数据库处理速度有关。提高数据库的处理数据的能力,其中一个方案就是sql语句的优化技术,sql语句写的处理效率比较高,数据库处理能力就会上去,而网站的数据处理能力也会快些。
但是,当网站的处理和访问量非常大的时候,我们的数据库的压力就变大了,数据库的连接池,数据库同时处理数据的能力就会受到很大的挑战,一旦数据库承受了其最大承受能力,网站的数据处理效率就会大打折扣。此时就要使用高并发处理、负载均衡和分布式数据库,而这些技术既花费人力,又花费资金。如果我们的网站不是非常大的网站,而有想要提高网站的效率,降低数据库的读写次数,我们就需要引入缓存技术。
缓存的作用是什么?
缓存就是在内存中存储的数据备份,当数据没有发生本质改变的时候,我们就不让数据的查询去数据库进行操作,而去内存中取数据,这样就大大降低了数据库的读写次数,而且从内存中读数据的速度比去数据库查询要快一些,这样同时又提高了效率。
Redis介绍
我们要学习的一个缓存技术就是----Redis是Remote Dictionary Server(远程数据服务)的缩写,由意大利人antirez(Salvatore Sanfilippo)开发的一款内存高速缓存数据库,该软件使用C语言编写,它的数据模型为key-value。它支持丰富的数据结构(类型),比如String/List/Hash/Set/Sorted Set。可持久化(一边运行,一边把数据往硬盘中备份一份,防止断电等情况导致数据丢失,等断电情况恢复之后,Redis再把硬盘中的数据恢复到内存中),保证了数据的安全。
首先在maven项目中的pom.xml文件中引入依赖
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE
com.example
demo
0.0.1-SNAPSHOT
demo
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
redis.clients
jedis
2.9.0
junit
junit
4.12
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-starter-cache
com.alibaba
fastjson
1.2.28
src/main/java
**/*.*
src/main/resources
**/*.*
org.springframework.boot
spring-boot-maven-plugin
创建名为RedisCacheConfig的类,做为Redis的配置类。
package com.example.demo.util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
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 redis.clients.jedis.JedisPoolConfig;
@Configuration
@EnableAutoConfiguration
public class RedisCacheConfig extends CachingConfigurerSupport {
/* @Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.database}")
private int database;
@Value("${spring.redis.password}")
private String password;*/
@Bean
public JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration =
new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName("192.168.30.15");
redisStandaloneConfiguration.setDatabase(0);
//redisStandaloneConfiguration.setPassword(RedisPassword.of("123456"));
redisStandaloneConfiguration.setPort(6379);
return new JedisConnectionFactory(redisStandaloneConfiguration);
}
@Bean
public StringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
/**
* 连接池配置信息
* @return
*/
@Bean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//最大连接数
jedisPoolConfig.setMaxTotal(100);
//最小空闲连接数
jedisPoolConfig.setMinIdle(20);
//当池内没有可用的连接时,最大等待时间
jedisPoolConfig.setMaxWaitMillis(10000);
//------其他属性根据需要自行添加-------------
return jedisPoolConfig;
}
/**
* jedis连接工厂
* @param jedisPoolConfig
* @return
*/
@Bean
public RedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPoolConfig) {
//单机版jedis
RedisStandaloneConfiguration redisStandaloneConfiguration =
new RedisStandaloneConfiguration();
//设置redis服务器的host或者ip地址
redisStandaloneConfiguration.setHostName("192.168.30.15");
//设置默认使用的数据库
redisStandaloneConfiguration.setDatabase(0);
//设置密码
//redisStandaloneConfiguration.setPassword(RedisPassword.of("123456"));
//设置redis的服务的端口号
redisStandaloneConfiguration.setPort(6379);
//获得默认的连接池构造器(怎么设计的,为什么不抽象出单独类,供用户使用呢)
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb =
(JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)JedisClientConfiguration.builder();
//指定jedisPoolConifig来修改默认的连接池构造器(真麻烦,滥用设计模式!)
jpcb.poolConfig(jedisPoolConfig);
//通过构造器来构造jedis客户端配置
JedisClientConfiguration jedisClientConfiguration = jpcb.build();
//单机配置 + 客户端配置 = jedis连接工厂
return new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
}
}
创建Redis工具类RedisUtil
package com.example.demo.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.entity.Car;
/**
*
* @Description: spring boot 的redis工具类
*/
@SuppressWarnings("unchecked")
@Component
public class RedisUtil {
@SuppressWarnings("rawtypes")
@Autowired
private RedisTemplate redisTemplate;
/**
* 批量删除对应的value
*
* @param keys
*/
public void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
}
/**
* 批量删除key
*
* @param pattern
*/
public void removePattern(final String pattern) {
Set keys = redisTemplate.keys(pattern);
if (keys.size() > 0)
redisTemplate.delete(keys);
}
/**
* 删除对应的value
*
* @param key
*/
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
/**
* 判断缓存中是否有对应的value
*
* @param key
* @return
*/
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
/**
* 读取缓存
*
* @param key
* @return
*/
public String get(final String key) {
Object result = null;
redisTemplate.setValueSerializer(new StringRedisSerializer());
ValueOperations operations = redisTemplate.opsForValue();
result = operations.get(key);
if (result == null) {
return null;
}
return result.toString();
}
/**
* 写入缓存
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperations operations = redisTemplate.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 写入缓存
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value, Long expireTime) {
boolean result = false;
try {
ValueOperations operations = redisTemplate.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public boolean hmset(String key, Map value) {
boolean result = false;
try {
redisTemplate.opsForHash().putAll(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public Map hmget(String key) {
Map result = null;
try {
result = redisTemplate.opsForHash().entries(key);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
创建实体类Car
package com.example.demo.entity;
public class Car {
private Integer id;
private String name;
private String vin;
private String platenumber;
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 String getVin() {
return vin;
}
public void setVin(String vin) {
this.vin = vin;
}
public String getPlatenumber() {
return platenumber;
}
public void setPlatenumber(String platenumber) {
this.platenumber = platenumber;
}
}
创建CarController类
package com.example.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.demo.entity.Car;
import com.example.demo.service.CarService;
@Controller
@RequestMapping("/car")
public class CarController {
@Autowired
private CarService carService;
@ResponseBody
@RequestMapping("/findById")
public Car findCarById(@RequestParam int id){
Car car = carService.findById(id);
return car;
}
@ResponseBody
@RequestMapping("/updateCar")
public String updateCar(@RequestParam int id){
Car car = new Car();
car.setId(id);
car.setName("奥迪");
car.setPlatenumber("W2K3111");
car.setVin("120056");
int result = carService.updateCar(car);
if(result == 1){
return "update car success";
}
return "update car error";
}
@ResponseBody
@RequestMapping("/deleteById")
public String deleteCarById(@RequestParam int id){
int result = carService.deleteById(id);
if(result == 1){
return "delete car success";
}
return "delete car error";
}
}
创建接口CarService
package com.example.demo.service;
import com.example.demo.entity.Car;
public interface CarService {
Car findById(int id);
int updateCar(Car car);
int deleteById(int id);
}
创建实现类
package com.example.demo.service.impl;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.entity.Car;
import com.example.demo.service.CarService;
import com.example.demo.util.RedisUtil;
@Service
public class CarServiceImpl implements CarService{
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private RedisUtil redisUtil;
/**
* 先从缓存中获取车辆信息,没有则取数据表中的数据,然后再将数据写入缓存中
*/
public Car findById(int id) {
String key = "car_" + id;
// ValueOperations operations = redisTemplate.opsForValue();
boolean hasKey = redisUtil.exists(key);
if (hasKey) {
String carStr = redisUtil.get(key);
JSONObject jsonObject=JSONObject.parseObject(carStr);
Car car=jsonObject.toJavaObject(Car.class);
System.out.println("==========从缓存中获得数据=========");
System.out.println(car.getId());
System.out.println(car.getName());
System.out.println(car.getPlatenumber());
System.out.println(car.getVin());
return car;
} else {
System.out.println("==========从数据表中获得数据=========");
//模拟数据库数据
Car car = new Car();
car.setId(1);
car.setName("大众");
car.setPlatenumber("W2YHF10");
car.setVin("120051");
// 写入缓存
redisUtil.set(key, JSON.toJSONString(car));
return car;
}
}
/**
* 先更新数据表,成功之后,删除原来的缓存,再更新缓存
*/
@Override
public int updateCar(Car car) {
try {
ValueOperations operations = redisTemplate.opsForValue();
System.out.println("==========数据库中更新车辆信息=========");
String key = "car_" + car.getId();
boolean haskey = redisUtil.exists(key);
if (haskey) {
redisUtil.remove(key);
System.out.println("删除缓存中的key=========>" + key);
}
// 再将更新后的数据加入缓存
redisUtil.set(key, JSON.toJSONString(car));
return 1;
}catch (Exception e) {
return 0;
}
}
/**
* 删除用户策略:删除数据表中数据,然后删除缓存
*/
@Override
public int deleteById(int id) {
try {
System.out.println("==========数据库中删除车辆信息=========");
String key = "car_" + id;
boolean haskey = redisUtil.exists(key);
if (haskey) {
redisUtil.remove(key);
System.out.println("删除了缓存中的key:" + key);
}
return 1;
}catch (Exception e) {
return 0;
}
}
}
启动SpringBoot项目,用Postman测试
发起http://localhost:8080/car/findById?id=1第一次请求,缓存中没有数据所以返回“从数据表中获得数据”
再次发起http://localhost:8080/car/findById?id=1请求,返回从”缓存中获得数据“
发起http://localhost:8080/car/updateCar?id=1更新请求,更新成功后,
再次发起http://localhost:8080/car/findById?id=1请求,从缓存中获得更新后的数据
发起http://localhost:8080/car/deleteById?id=1删除请求后,在redis数据库中删除该数据