1.我的选课
在进行选课活动中了为了尽量减少客户端和数据库的连接次数,在实现的思路是这样设计的:客户端的请求优先访问Redis缓存空间,如果Redis中没有则从数据库中加载,并添加到Redis中。这样多个客户端会优先从Redis中加载所需的资源,减少了数据库的压力。
ps:选课中的课程比较多,数据库量较大。
在Redis的存储遇到了个问题,因为Redis的存储方式是(Key,Value)形式的,在设计中Key采用自定义+课程ID的方式,而Value是String类型的,但是从数据库中查询出来的数据是List形式的。Redis本身是不支持这种数据库类型的存储的。
2.Redis五种存储类型
Redis常用五种数据类型:string,hash,list,set,zset(sorted set).
1.String类型
String是最简单的类型,一个key对应一个value
String类型的数据最大1G。
String类型的值可以被视作integer,从而可以让“INCR”命令族操作(incrby、decr、decrby),这种情况下,该integer的值限制在64位有符号数。
在list、set和zset中包含的独立的元素类型都是redis String类型。2.List类型
链表类型,主要功能是push、pop、获取一个范围的所有值等。其中的key可以理解为链表的名字。
在Redis中,list就是Redis
String的列表,按照插入顺序排序。比如使用LPUSH命令在list头插入一个元素,使用RPUSH命令在list的尾插入一个元素。当这两个命令之一作用于一个空的key时,一个新的list就创建出来了。List的最大长度是2^32-1个元素。
3.Set类型
集合,和数学中的集合概念相似。操作中的key理解为集合的名字。
在Redis中,set就是Redis String的无序集合,不允许有重复元素。
Set的最大元素数是2^32-1。
Redis中对set的操作还有交集、并集、差集等。
4.ZSet(Sorted Set)类型
Zset是set的一个升级版本,在set的基础上增加了一个顺序属性,这一属性在添加修改元素时可以指定,每次指定后zset会自动安装指定值重新调整顺序。可以理解为一张表,一列存value,一列存顺序。操作中的key理解为zset的名字。
Zset的最大元素数是2^32-1。
对于已经有序的zset,仍然可以使用SORT命令,通过指定ASC|DESC参数对其进行排序。
5.hash类型
hash是最接近关系数据库结构的数据类型,可以将数据库一条记录或程序中一个对象转换成hashmap存放在redis中。
3.序列化类
- 如果要满足需求进行List的存储,则需要进行序列化,既将List序列化成字符进行存储。这里就需要借助序列化的工具类了。序列化工具类SerializeUtil如下:
package com.dmsdbj.itoo.teachingManagement.service.impl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 序列化对象工具类,用于保存和读取redis数据使用
* Created by F-mdh on 2017/7/9.
*/
public class SerializeUtil {
private static Logger log = LoggerFactory.getLogger(SerializeUtil.class);
/**
* 序列化对象
* @param object
* @return
*/
public static byte[] serialize(Object object) {
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null;
byte[] bytes = null;
try {
// 序列化
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
bytes = baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (oos != null) {
oos.close();
}
if (baos != null) {
baos.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return bytes;
}
/**
* 反序列化对象
* @param bytes
* @return
*/
public static Object unserialize(byte[] bytes) {
Object obj = null;
ByteArrayInputStream bais = null;
try {
// 反序列化
bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
obj = ois.readObject();
ois.close();
bais.close();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
/**
* 关闭的数据源或目标。调用 close()方法可释放对象保存的资源(如打开文件)
* 关闭此流并释放与此流关联的所有系统资源。如果已经关闭该流,则调用此方法无效。
* @param closeable
*/
public static void close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception e) {
log.info("Unable to close %s", closeable, e);
}
}
}
/**
* 列表序列化(用于Redis整存整取)
* @param value
* @return
*/
public static <T> byte[] serialize(List<T> value) {
if (value == null) {
throw new NullPointerException("Can't serialize null");
}
byte[] rv=null;
ByteArrayOutputStream bos = null;
ObjectOutputStream os = null;
try {
bos = new ByteArrayOutputStream();
os = new ObjectOutputStream(bos);
for(T obj : value){
os.writeObject(obj);
}
os.writeObject(null);
os.close();
bos.close();
rv = bos.toByteArray();
} catch (IOException e) {
throw new IllegalArgumentException("Non-serializable object", e);
} finally {
close(os);
close(bos);
}
return rv;
}
/**
* 反序列化列表(用于Redis整存整取)
* @param in
* @return
*/
public static <T> List<T> unserializeForList(byte[] in) {
List<T> list = new ArrayList<T>();
ByteArrayInputStream bis = null;
ObjectInputStream is = null;
try {
if(in != null) {
bis=new ByteArrayInputStream(in);
is=new ObjectInputStream(bis);
while (true) {
T obj = (T) is.readObject();
if(obj == null){
break;
}else{
list.add(obj);
}
}
is.close();
bis.close();
}
} catch (IOException e) {
log.warn("Caught IOException decoding %d bytes of data",
in == null ? 0 : in.length, e);
} catch (ClassNotFoundException e) {
log.warn("Caught CNFE decoding %d bytes of data",
in == null ? 0 : in.length, e);
} finally {
close(is);
close(bis);
}
return list;
}
}
4.代码
/**
*从redis中 查询所有的公选数据:
* @param :
* @return
*/
@Override
public List<ChooseCourseModel> queryAllCoursePage() {
//获取公共选修课性质
String coursetype="3B6FE3BAB12E3BA8AFE36D";
//1.接受数据volist
List<ChooseCourseModel> chooseCourseModelList = new ArrayList<>();
//2.实例化redis
//创建一个spring容器
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:redis.xml");
//从容器中获得JedisClient对象
JedisClientCluster jedisClientCluster=(JedisClientCluster)applicationContext.getBean("jedisClientCluster");
//3.获取查询条件
//4.从redis查询数据
String key = "tm:ChooseCourseServiceImpl:publicchoosecourse";
byte[] in= jedisClientCluster.get(key.getBytes());//拿到数据
chooseCourseModelList = SerializeUtil.unserializeForList(in);//反序列化,赋值volist
//4.1 if yes 判断拿出的数据
if (chooseCourseModelList.isEmpty() || "".equals(chooseCourseModelList)) {
//4.2 if no 查数据库
chooseCourseModelList = chooseCourseDao.queryAllCoursePage(coursetype);
//4.2.1 判断是否为空
//4.2.2 放入redis中
jedisClientCluster.set(key.getBytes(), SerializeUtil.serialize(chooseCourseModelList));
}
return chooseCourseModelList;
}
调用工具类:
序列化:
jedisClientCluster.set(key.getBytes(), SerializeUtil.serialize(chooseCourseModelList));
反序列化:
chooseCourseModelList = SerializeUtil.unserializeForList(in);//反序列化,赋值volist