情景:
当查询数据库的时候,把查询结果保存到Redis中,下次再查询的时候,先查询redis缓存中是否存在用户查询的数据,有则直接返回,没有再去查数据库,结果再保存到数据库中。现实中的应用场景,比如某明星,很多粉丝都查询这个明星的信息,同样的信息一条可能查几百万次,这种情况就可以用这种缓存。
步骤:
搭建SSM框架:具体可以参考另一篇博客https://blog.csdn.net/DGH2430284817/article/details/88587804
创建数据库(Student):
随便添加一条数据就可以了
用SqlMap工程创建对应的po类和Mapper和数据库XML文件:
创建控制器StudentController.java:
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.ceb.dgh.bo.Student;
import com.qut.util.LoggerUtil;
import com.test.service.StudentService;
@RequestMapping("/Stu")
@Controller
public class StudentController {
private static Logger log = LoggerUtil.getLogger(StudentController.class);
@Autowired
private StudentService studentService;
@RequestMapping(value = "findOneNoRedis",method = {RequestMethod.POST})
@ResponseBody
public String findOneNoRedis(@RequestBody String clientMsg){//不用redis来缓存数据
log.info("进入控制器findOneNoRedis,参数:" + clientMsg);
Student student = studentService.findOneNoRedis(Integer.valueOf(clientMsg));
String returnMsg = student.toString();//相当于业务操作结果
return returnMsg;
}
@RequestMapping(value = "findOneByRedis",method = {RequestMethod.POST})
@ResponseBody
public String findOneByRedis(@RequestBody String clientMsg){//用redis来缓存数据
log.info("进入控制器findOneByRedis,参数:" + clientMsg);
Student student = studentService.findOneByRedis(Integer.valueOf(clientMsg));
String returnMsg = student.toString();//相当于业务操作结果
return returnMsg;
}
}
业务逻辑层和实现类:
StudentService
import com.ceb.dgh.bo.Student;
public interface StudentService {
public Student findOneNoRedis(Integer id);//不用redis来缓存数据
public Student findOneByRedis(Integer id);//用redis来缓存数据
}
StudentServiceImpl.java
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ceb.dgh.bo.Student;
import com.ceb.dgh.mapper.StudentMapper;
import com.qut.util.LoggerUtil;
import com.test.service.StudentService;
import cn.e3mall.common.jedis.JedisClient;
@Service
@Transactional(rollbackFor=Exception.class)
public class StudentServiceImpl implements StudentService{
private static Logger log = LoggerUtil.getLogger(StudentServiceImpl.class);
@Autowired
private StudentMapper studentMapper;
private boolean temp = true;
private JedisClient jedisClient ;
private String REDIS_CONTENT_KEY = "Student";
@Override
public Student findOneByRedis(Integer id) {//用redis来缓存数据
if (temp) {//只加载一次
System.out.println("加载redis!");
//初始化Spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext-redis.xml");
//从容器中获得JedisClient对象
jedisClient = applicationContext.getBean(JedisClient.class);
temp = false;//下次进入后不会再进入
}
// 查询数据时,先从缓存查询,有就直接返回
try {
String stuMsg = jedisClient.hget(REDIS_CONTENT_KEY, id + "");
if (stuMsg != null && !"".equals(stuMsg)) {
log.info("redis有该学生信息");
return new Student( id , stuMsg);
}
} catch (Exception e1) {
e1.printStackTrace();
}
log.info("用Redis缓存");
Student reStudent = studentMapper.selectByPrimaryKey(id);
// 查询结果再放入缓存
try {
jedisClient.hset(REDIS_CONTENT_KEY, id + "",
reStudent.getName());
} catch (Exception e) {
e.printStackTrace();
}
return reStudent;
}
@Override
public Student findOneNoRedis(Integer id) {//不用redis缓存
log.info("不用Redis缓存");
Student reStudent = studentMapper.selectByPrimaryKey(id);
return reStudent;
}
}
在业务实现类中,有段代码是用来加载redis的:
if (temp) {//只加载一次
System.out.println("加载redis!");
//初始化Spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext-redis.xml");
//从容器中获得JedisClient对象
jedisClient = applicationContext.getBean(JedisClient.class);
temp = false;//下次进入后不会再进入
}
加载的方式不建议用这种,应该整合到spring中,我偷个懒,具体的redis的接口封装网上有很多,jedisClient 对象里的方法都是一样的。
测试类TestClient.java:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.nio.charset.Charset;
import java.net.URL;
import org.springframework.util.Assert;
public class TestClient {
public static void main(String[] args) throws Exception {
TestClient aaClient = new TestClient();
//String Msg = "client sent to server";
String Msg = "1";//查询数据库表Student的ID
String resString =aaClient.doPostRequest("http://localhost:8080/Maven_SSM/Stu/findOneByRedis.action", Msg, Charset.forName("UTF-8"), 10, 10);
long startTime = System.currentTimeMillis();
for(int i =0 ; i < 3000 ; i++){
resString =aaClient.doPostRequest("http://localhost:8080/Maven_SSM/Stu/findOneNoRedis.action", Msg, Charset.forName("UTF-8"), 10, 10);
//System.out.println("返回Student信息:" + resString);这一行我注释了,因为打印上千条信息到控制台也很浪费时间,影响因素大,会让我们很难直观的查看Redis的优化效果
}
System.out.println("不用Redis查询3000次完成用时(ms):"+(System.currentTimeMillis()-startTime));
startTime = System.currentTimeMillis();
for(int i =0 ; i < 3000 ; i++){
resString =aaClient.doPostRequest("http://localhost:8080/Maven_SSM/Stu/findOneByRedis.action", Msg, Charset.forName("UTF-8"), 10, 10);
//System.out.println("返回Student信息:" + resString);这一行我注释了,因为打印上千条信息到控制台也很浪费时间,影响因素大,会让我们很难直观的查看Redis的优化效果
}
System.out.println("用Redis查询3000次完成用时(ms):"+(System.currentTimeMillis()-startTime));
}
/**
* @param reqUrl 地址
* @param params 数据(json)
* @param charset 编码
* @param readTimeSec
* @param connTimeSec
* @return Http请求返回数据字符串
* @throws Exception
*/
public static String doPostRequest(String reqUrl, String params,
Charset charset, int readTimeSec, int connTimeSec)
throws Exception {
try {
Assert.hasText(reqUrl, "请求地址为空");
Assert.hasText(params, "请求数据为空");
} catch (Exception e) {
throw new Exception("发送请求参数为空:"
+ e.getMessage(), e);
}
String request = params ;//发送服务端数据
HttpURLConnection httpConnection = null;
try {
httpConnection = (HttpURLConnection) new URL(reqUrl)
.openConnection();
httpConnection.setRequestMethod("POST");
httpConnection.setDoOutput(true);
httpConnection.setDoInput(true);
httpConnection.setUseCaches(false);
httpConnection.setRequestProperty("Connection", "Keep-Alive");
httpConnection.setRequestProperty("Charset", charset.displayName());
httpConnection.setRequestProperty("Content-Type",
"application/json; charset=" + charset.displayName());
httpConnection.setRequestProperty("accept", "application/json");
httpConnection.setReadTimeout(readTimeSec * 1000);// 设置http连接的读超时,单位是毫秒
httpConnection.setConnectTimeout(connTimeSec * 1000);
// 1、连接
try {
httpConnection.connect();
} catch (Exception e) {
throw new Exception(
"服务器连接是失败");
}
OutputStream outwritestream = null;
// 2、发送数据
try {
//System.out.println("请求数据:"+request);
byte[] writebytes = request.getBytes(charset);
outwritestream = httpConnection.getOutputStream();
outwritestream.write(writebytes);
outwritestream.flush();
} catch (Exception e) {
throw new Exception(
"请求数据发送失败", e);
} finally {
try {
outwritestream.close();
} catch (Exception e) {
}
}
return getHttpReturn(httpConnection , charset);
} catch (Exception e) {
throw new Exception("服务器连接失败:"
+ e.getMessage(), e);
} finally {
if (httpConnection != null) {
httpConnection.disconnect();
}
}
}
/**
* @param httpConnection
* @return Http连接返回数据转化成字符串
* @throws Exception
*/
private static String getHttpReturn(HttpURLConnection httpConnection , Charset charset)
throws Exception {
int responseCode = -1;
try {
responseCode = httpConnection.getResponseCode();
//System.out.println("responseCode:"+responseCode);
} catch (Exception e) {
throw new Exception(
"接收数据超时", e);
}
try {
Assert.isTrue(responseCode == 200, "服务器响应码【"
+ responseCode + "】");
} catch (Exception e) {
throw new Exception(
"接收数据失败:" + e.getMessage(), e);
}
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(
httpConnection.getInputStream(), charset));
String str = "";
String temp;
while((temp = reader.readLine())!=null){
str = str + temp +"\r\n" ;
}
return str;
} catch (Exception e) {
throw new Exception(
"接收数据超时:" + e.getMessage(), e);
} finally {
try {
if (null != reader) {
reader.close();
}
} catch (Exception e) {
}
}
}
}
右键运行测试(前提是开启了Redis服务器和SSM项目服务器):
测试结果:
服务端:
客户端:
不用Redis查询3000次完成用时(ms):13644
用Redis查询3000次完成用时(ms):8457
也可以把所有的日子打印控制台打印全部都注释,这样可以更加精确的看出Redis对系统的性能提高:
所有控制台打印和日志注释后结果:
服务端:
客户端:
不用Redis查询3000次完成用时(ms):9733
用Redis查询3000次完成用时(ms):5146