1、什么是Redis:
Redis是由意大利人Salvatore Sanfilippo(网名:antirez)开发的一款内存高速缓存数据库。Redis全称为:Remote Dictionary Server(远程数据服务),该软件使用C语言编写,Redis是一个key-value存储系统,它支持丰富的数据类型,如:string、list、set、zset(sorted set)、hash。
2、Redis特点:
Redis以内存作为数据存储介质,所以读写数据的效率极高,远远超过数据库。以设置和获取一个256字节字符串为例,它的读取速度可高达110000次/s,写速度高达81000次/s。
3、Redis缺点:
3.1. 由于是内存数据库,所以,单台机器,存储的数据量,跟机器本身的内存大小。虽然redis本身有key过期策略,但是还是需要提前预估和节约内存。如果内存增长过快,需要定期删除数据。
3.2. 如果进行完整重同步,由于需要生成rdb文件,并进行传输,会占用主机的CPU,并会消耗现网的带宽。不过redis2.8版本,已经有部分重同步的功能,但是还是有可能有完整重同步的。比如,新上线的备机。
3.3. 修改配置文件,进行重启,将硬盘中的数据加载进内存,时间比较久。在这个过程中,redis不能提供服务。
4、为什么要使用Redis:
当大量用户使用同一个查询功能,程序内部就会调用同一个查询方法,每次都会向服务器请求数据,可能会出现响应慢或者加载失败的情况,效率极低。使用redis后,只要有一个用户使用这个查询,会先通过redis的key去redis里面找,如果没找到则会向数据库去查询,当查询完成后结果会保存到redis里面,redis再返回出去。当后面的用户使用同一个查询时就会使用redis里的数据,不会再去向数据库去查询,提高了效率。
5、为什么要使用AOP来实现Redis
因为使用AOP可以在不改变原来代码的基础上增加新功能
6、具体的代码实现
使用到的jar包:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>3.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.2.13.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>3.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.3</version>
</dependency>
</dependencies>
spring 配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<aop:aspectj-autoproxy/> <!-- 声明切面代理 -->
<context:annotation-config/> <!-- 向spring注册容器,容器提供相对应的注解 -->
<context:component-scan base-package="com.ketai.liudun"/> <!-- 告诉spring要扫描的包 -->
<!-- 配置Jedis -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="20"></property> <!-- 最大连接数 -->
<property name="maxIdle" value="10"></property> <!-- 最大空闲连接数 -->
<!-- MaxWait 连接池中连接用完时,新的请求等待时间,毫秒,取值-1,表示无限等待,直到超时为止,
也可取值9000,表示9秒后超时。超过时间会出错误信息一般把maxActive设置成可能的并发量就行了 -->
<property name="MaxWaitMillis" value="1000"></property>
<property name="testOnBorrow" value="true"></property>
</bean>
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<!--通过构造注入,index == 参数下标 -->
<constructor-arg index="0" ref="jedisPoolConfig" /><!-- 加载jedisPool配置信息 -->
<constructor-arg index="1" value="127.0.0.1" /><!-- redis 主机地址 -->
<constructor-arg index="2" value="6379" /><!-- redis 连接端口 -->
<!-- timeout -->
<constructor-arg index="3" value="2000" /> <!-- 超时时间 -->
<!-- password -->
<constructor-arg index="4" value="IT_training_CZKT_is_Best!" /> <!-- 配置密码 -->
</bean>
<import resource="spring-mybatis.xml"/>
</beans>
测试类:
package com.ketai.liudun;
import static org.junit.Assert.assertNotNull;
import org.aspectj.lang.annotation.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.ketai.liudun.entity.User;
import com.ketai.liudun.service.UserService;
import com.ketai.liudun.service.impl.UserServiceImpl;
public class TestAspectOfCache {
private UserService userService;
/**
* 在执行查询前先获得spring配置文件
* @throws Exception
*/
@Before
public void setUp() throws Exception {
ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext();
ac.setValidating(false);
ac.setConfigLocation("spring-beans.xml");
ac.refresh();
System.out.println("Spring IoC container initialized.");
userService = (UserService)ac.getBean("userService");
assertNotNull(userService);
System.out.println("userService initialized.");
}
@Test
public void testFindById() {
User u = userService.findUser(78);
assertNotNull(u);
System.out.println(u);
}
// @Test
// public void testAddUser(){
// User user = new User();
// user.setUserName("zhangjun");
// user.setPassword("333");
// userService.addUser(user);
// }
public void setUserService(UserService userService) {
this.userService = userService;
}
}
redis实现类:
package com.ketai.liudun.cache;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
@Component
public class RedisCache {
@Resource
private JedisPool jedisPool;
public Object getFromCache(String key){
Jedis jedis = jedisPool.getResource(); //连接到redis
byte[] result = jedis.get(key.getBytes());//通过key来获得值,保存在byte数组里面
//如果查询没有为空
if(null == result){
return null;
}
//查询到了,反序列化
return SerializeUtil.unSerialize(result);
}
//将数据库中查询到的数据放入redis
public void save(String redisKey, Object obj){
//序列化
byte[] bytes = SerializeUtil.serialize(obj);
//存入redis
Jedis conn = jedisPool.getResource();
String result = conn.set(redisKey.getBytes(), bytes);
if("OK".equals(result)){
System.out.println("[RedisCache]:数据成功保存到redis cache...");
}
}
}
序列工具类:
package com.ketai.liudun.cache;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializeUtil {
//序列化
public static byte[] serialize(Object obj){
ObjectOutputStream obi=null;
ByteArrayOutputStream bai=null;//byte类型数字缓冲区
try {
bai=new ByteArrayOutputStream();
obi=new ObjectOutputStream(bai);
obi.writeObject(obj);
byte[] byt=bai.toByteArray();
return byt;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//反序列化
public static Object unSerialize(byte[] byt){
ObjectInputStream oii=null;
ByteArrayInputStream bis=null;
bis=new ByteArrayInputStream(byt);
try {
oii=new ObjectInputStream(bis);
Object obj=oii.readObject();
return obj;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
AOP增强类:
package com.ketai.liudun.cache;
import javax.annotation.Resource;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 该类表示一个切面,表示:
* “调用find*方法根据id查某个对象时,先从缓存查询,如果没有,再查数据库”
*
*/
@Component
@Aspect
public class RedisCacheAspect {
private static String PROJECT_NAME = "ITRIP-";
private static String MODULE_NAME = "AUTH-";
private static final Logger log =
Logger.getLogger(RedisCacheAspect.class);
@Resource
private RedisCache redisCache;
@Pointcut("execution(* com.ketai.liudun.service..*.find*(..))")
public void getCachePointcut(){}
@Around("getCachePointcut()")
public Object around(ProceedingJoinPoint joinPoint){
//先获取目标方法参数
String id = null;
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
id = String.valueOf(args[0]);
}
//得到一个具有唯一性的缓存key
String redisKey = getRedisKey(joinPoint, id);
log.debug("进入环绕增强前半部分,尝试从redis查询,生成的缓存key:"+redisKey);
//根据key获取从redis中查询到的对象
Object data = redisCache.getFromCache(redisKey);
//如果查询到了
if(null != data){
log.debug("["+redisKey+"]缓存命中:"+data);
return data;
}
//没有查到,那么查询数据库
try {
data = joinPoint.proceed();//执行目标方法(service中的find*方法)
} catch (Throwable e) {
e.printStackTrace();
}
log.debug("没有从redis中取出数据,改为从数据库中查询的数据..."+data);
//后置:将数据库中查询的数据放到redis中
redisCache.save(redisKey, data);
log.debug("数据已存储到redis"+data);
//将查询到的数据返回
return data;
}
private String getRedisKey(ProceedingJoinPoint joinPoint, String id) {
return PROJECT_NAME + MODULE_NAME
+ joinPoint.getTarget().getClass().getName() + "-" +id;
}
}