您的位置:首页 > 数据库 > Redis

redis value为对象的排序、分页的一种实现

2012-11-29 19:41 363 查看

-------基于HBase存储引擎并实现了排序、分页的Redis缓存策略

思路:

1、
当前系统缓存json,根据key存取value,key
和value都是String类型,直接返回前端。

2、
考虑支持分页,想到了缓存排好序的ResultSet,每一次前端请求,将所有数据缓存到redis,根据filter返回某页的数据,此部分数据是封装好的json。

考虑到使用list会增加开发的工作量,所以暂不用。

3、
考虑缓存ResultSet对象,所以该类要实现序列化,用hadoop的Writable,不用java本身的Serializable。

4、
前端需求不需要分页的仍然用原来接口,缓存json,用不同的annotation设置到查询方法上区分。

注:ResultSet为自己实现了的将hbase列存储数据转为按关系数据库行存储的数据结构。

具体实现:

1、由于每次都传给前端少量数据,所以要传给前端总记录条数方便其确定分页信息,可封转在json里。

2、rediscache操作类增加支持序列化的存取方法:

@Override
public ResultSet getResultSetCache(String key) {
try {
log.info("get object from redisCache :"+key);
ShardedJedis jedis = pool.getResource();
ResultSet value = SerializableUtils.deSerialize(jedis.get(key.getBytes()), ResultSet.class);
pool.returnResource(jedis);
return value;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

@Override
public boolean setResultSetCache(String key, ResultSet value) {
try {
log.info("add object to redisCache :"+key);
ShardedJedis jedis = pool.getResource();
jedis.set(key.getBytes(),SerializableUtils.serialize(value));
pool.returnResource(jedis);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}


其中SerializableUtils工具类实现对writable子类的序列化、反序列化操作:

public class SerializableUtils {

public static<T extends Writable> byte[] serialize(Object value) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();

DataOutputStream oos = new DataOutputStream(bos);
if (value instanceof Writable) {
((T) value).write(oos);
} else {
throw new IOException("not instanceof Writable!");
}
return bos.toByteArray();
}

public static <T extends Writable> T deSerialize(byte[] byteCode,Class<T> classType) throws IOException{
ByteArrayInputStream bis = new ByteArrayInputStream(byteCode);
T object = null;
try {
DataInputStream dis = new DataInputStream(bis);
object = (T)classType.newInstance();
object.readFields(dis);
return object;
} catch (IOException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

return null;
}

}


java本身的序列化反序列化工具类:

public class SerializableUtil {

public static<T extends Serializable> byte[] serialize(Object value){
ByteArrayOutputStream bos = new ByteArrayOutputStream();

try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(value);
} catch (IOException e) {
e.printStackTrace();
}
return bos.toByteArray();
}

public static <T extends Serializable> T deSerialize(byte[] bytes){
ByteArrayInputStream bIs = new ByteArrayInputStream(bytes);
try {
ObjectInputStream oIs = new ObjectInputStream(bIs);
return (T)oIs.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}


3、 增加两个annotation及两个切面处理类,annotation用以在具体查询方法上添加,区分需不需要排序,需要排序的缓存ResultSet,不需要的缓存Json。

需要分页的切面类主要代码:

String md5key = MD5Util.getMD5(filter.getValue().toString());
Object value = redisCacheManager.getResultSetCache(md5key);
String srcClzzName = "";
boolean flag = false;
if (null != value) {
srcClzzName = pjp.getTarget().getClass().getSimpleName();
JSONObject json = redisCacheManager.getJson(value,filter,srcClzzName);
return json;
} else if ("null".equals(value)) {
return null;
} else { //执行hbase查询

ResultSet orderRs = redisCacheManager.getResultSet(filter,srcClzzName);
boolean ifNeedCache = orderRs.size()>0?true:false;
if (ifNeedCache == true){
redisCacheManager.setResultSetCache(md5key, orderRs);
}
return redisCacheManager.getJson(orderRs, filter, srcClzzName);
}


getResultSet部分代码:

public ResultSet getOrderdResultSet(QueryFilter filter) {

SaleDetailsFilter Salefilter = (SaleDetailsFilter) filter;
String shopId = Salefilter.getShopId();
String date = Salefilter.getDate();//
int proType = Salefilter.getProType();//
byte sku = 1, spu = 0;
byte isSKU = Salefilter.getIsMergeSKU() ? spu : sku;
long[] proNum = Salefilter.getProNums();//
String[] itemNums = Salefilter.getItemNums();//
ResultSet result = new ResultSet();

try {
// set start row;
ByteBuffer rowkey = ByteBuffer.allocate(13);
rowkey.putInt(Integer.parseInt(shopId));
rowkey.putInt(Integer.parseInt(date));
// set product type, default 0
rowkey.putInt(proType);
rowkey.put(isSKU);
byte[] endkey = CommonUtil.getEndKey(rowkey.array(),"int,int,int,byte");

Where where = null;
List<OrderBy> orderlist = null;

if ((proNum != null && proNum.length > 0)) {根据前端传入的商品id查询记录
//拼where条件

for (int i = 0; i < proNum.length; i++) {
if(i>0){
where = or(eq("d:ProID",proNum[i]),where);
continue;
}
where = eq("d:ProID",proNum[i]);
}
orderlist = new ArrayList<OrderBy>();
Map<String,Integer> ordermap = Salefilter.getOrderBy();
if(null!=ordermap){
Set<String> keys = ordermap.keySet();
for(Iterator<String> it = keys.iterator();it.hasNext();){
String key = it.next();
Integer value = ordermap.get(key);
if(value==0){
orderlist.add(asc(key));
}else if(value==1){
orderlist.add(desc(key));
}
}
result = client.simpleQuery(rowkey.array(),endkey, select, tableName, where, orderlist,-1);
}else{
result = client.simpleQuery(rowkey.array(),endkey, select, tableName, where, null,-1);
}
return result;
} else if (itemNums != null && itemNums.length > 0) {

.....			} else {
......			}

} catch (Exception e) {
logger.error(e.getMessage(), e);
} catch (Throwable e) {
logger.error(e.getMessage(), e);
e.printStackTrace();
}
return null;
}

当然,用到的很多方法在cp基础上自定义的对hbase的查询接口。

4、
查询接口类增加相应属性、方法,并且hbase表要加入coprocessor的支持,因为用到了聚合查询。

从缓存得到某一页的数据返回前端:

public JSONObject getPagedJson(Object value, QueryFilter filter,String srcClzzName) throws JSONException {

JSONObject result = new JSONObject();
ResultSet tempRs = (ResultSet) value;
long size = tempRs.size();
ResultSet realRs = new ResultSet();

SaleDetailsFilter sdf = (SaleDetailsFilter) filter;
int pageNum = sdf.getPageNum();
int start = (pageNum - 1) * PAGE;
int maxend = PAGE * pageNum-1;
long end = size-(PAGE*pageNum)>=0?maxend:start+size-(pageNum-1)*PAGE-1;
for (int i = start; i <= end; i++) {
realRs.add(tempRs.get(i));
}
JSONObject temp = new JSONObject();
if (realRs.size() > 0) {
temp = trans.toJSONObject(realRs,select);
}
result.put(Transformer.DTL, temp);
result.put(Transformer.SIZE, size);
return result;
}


5、
查询接口类对应的filter增加两个属性,orderby和pageNum。Orderby表示排序的列,map类型,key表示列,值为0或1,表示升序或降序,默认为空,表示不排序。PageNum表示页号,默认第一页。

并将orderby纳入到查询条件中作为生成redis key的json。

pageNum是不能纳入的,因为前端翻页时应该对应redis中的一份数据。redis中的数据的份数由查询的种类决定,每一份都是查询的全量数据。后续考虑用list或索引的方式实现。

6、
redis集群安装—略。

7、
redis java客户端配置文件在web-inf下,包括连接池配置,属性配置,切面配置。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: