package com.ycsoft.report.query.redis;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.ycsoft.commons.exception.ReportException;
import com.ycsoft.report.commons.ReportConstants;
import com.ycsoft.report.commons.SerializeUtil;
import com.ycsoft.report.dao.redis.JedisDao;
import com.ycsoft.report.query.cube.impl.AbstractDataSet;
import com.ycsoft.report.query.daq.DataReader;
import com.ycsoft.report.query.daq.translate.CacheAcquisition;
import com.ycsoft.report.query.daq.translate.CacheHeadCell;
import com.ycsoft.report.query.daq.translate.CacheKey;
import static com.ycsoft.report.commons.ReportConstants.memory_row_max;
/**
* 使用redis执行cube运算
* 当内存中计算的结果集大于1000条时,把结果转储到redis。
* CacheKey的排序交给mongodb实现
*/
public class RedisAcquisition extends CacheAcquisition {
private JedisDao jedisDao=null;
private String query_id=null;;
public RedisAcquisition(JedisDao jedisDao, String query_id){
this.jedisDao=jedisDao;
this.query_id= query_id;
}
/**
* RedisCacheKey含有在redis使用的键值
* @throws ReportException
*/
@Override
protected CacheKey createCacheKey(List<Object> keylist,List<Integer> sortlist) throws ReportException {
return new RedisCacheKey(keylist,sortlist,this.query_id);
}
/**
* 在redis的cube运算,当计算键值>memory_row_max时,使用redis缓存合并数据
* @return
*/
@Override
public DataReader translate(String query_cache_id, AbstractDataSet dataset)
throws ReportException {
List<CacheHeadCell> headlist=this.createCacheHeadCell(dataset);
int no_crosshead_start=this.getNoCrossheadStart(headlist);
//初始化排序器
sort=new CacheKeySortByDB(no_crosshead_start,this.query_id);
DataReader dr=super.translate(query_cache_id, dataset);
//计算结果还在内存中
if(this.useredis){
if(this.getMenoryMap().size()>0){
//this.mergeSet();
mergeSetNoBacth();
}
return new RedisDR();
}else{
return dr;
}
}
private CacheKeySortByDB sort=null;
private boolean useredis=false;
/**
* 当java内存中运算的cube运算键值达到memory_row_max限额时,
* 把运算键值提交给redis缓存合并;清空java内存中运算的cube运算键值
*/
@Override
protected boolean isMenoryMax() throws ReportException {
if(this.getMenoryMap().size()>memory_row_max){
//this.mergeSet();
mergeSetNoBacth();
this.getMenoryMap().clear();
if(!this.useredis){
this.useredis=true;
}
}
return false;
}
/**
* 本地缓存单条提交redis合并运算
* @throws ReportException
*/
private void mergeSetNoBacth() throws ReportException{
try{
//cube缓存redis的keys
List<CacheKey> sortdbkeys=new ArrayList<CacheKey>(ReportConstants.cache_batch_size);
Iterator<CacheKey> it= this.getMenoryMap().keySet().iterator();
while(it.hasNext()){
//提取一个内存中键值
RedisCacheKey key= (RedisCacheKey) it.next();
byte[] rediskey=key.getRediskey().getBytes();
byte[] redisvalue=this.jedisDao.get(rediskey);
ArrayList<Double> localValue=(ArrayList<Double>) this.getMenoryMap().get(key);
if(redisvalue==null){
//缓存不存在则提交缓存
this.jedisDao.set(rediskey, SerializeUtil.serialize(localValue));
sortdbkeys.add(key);
}else{
//合并本地
ArrayList<Double> serverValue= (ArrayList<Double>) SerializeUtil.unserialize(redisvalue);
for(int j=0;j<localValue.size();j++){
localValue.set(j,localValue.get(j).doubleValue()+serverValue.get(j).doubleValue());
}
this.jedisDao.set(rediskey, SerializeUtil.serialize(localValue));
}
//提交服务器
if(sortdbkeys.size()>=ReportConstants.cache_batch_size||!it.hasNext()){
sort.openWriteMode();
sort.writeCacheKey(sortdbkeys);
sortdbkeys.clear();
}
}
}catch(ReportException e){
sort.close();
throw e;
}catch(Exception e){
sort.close();
throw new ReportException(e);
}
}
/**
* 本地缓存批量提交redis合并运算
* <p> 存在bug
* @throws ReportException
*/
private void mergeSet() throws ReportException {
try{
//批量提交数量
int batch_idx=0;
//cube缓存redis的keys
ArrayList<byte[]> rediskeys=new ArrayList<byte[]>(ReportConstants.cache_batch_size);
//cube缓存redis的keysvalues
ArrayList<byte[]> rediskeyvalues=new ArrayList<byte[]>(ReportConstants.cache_batch_size*2);
//新计算出来的CacheKey列表
List<CacheKey> newcechekeys=new ArrayList<CacheKey>(ReportConstants.cache_batch_size);
Map<Integer,CacheKey> newcechekeyMap=new HashMap<Integer,CacheKey>(ReportConstants.cache_batch_size);
Iterator<CacheKey> it= this.getMenoryMap().keySet().iterator();
while(it.hasNext()){
//提取一个内存中键值
RedisCacheKey key= (RedisCacheKey) it.next();
rediskeys.add(key.getRediskey().getBytes());
newcechekeyMap.put(batch_idx, key);
batch_idx++;
//当从内存提取键值达到批量提交限额或元素迭代完时,提交redis缓存并合并相同的key对应的计算结果
if(batch_idx>=ReportConstants.cache_batch_size||!it.hasNext()){
batch_idx=0;//从新计数
//从redi获取keys对应的values,如果获取的值为空,说明redis上不存在这个值的缓存
List<byte[]> redisvalues=this.jedisDao.mget(rediskeys.toArray(new byte[rediskeys.size()][]));
for(int i=0;i<rediskeys.size();i++){
byte[] value=redisvalues.get(i);
ArrayList<Double> cubeValue=(ArrayList<Double>) this.getMenoryMap().get(key);
if(value!=null){
//如果value不为空,redis获取的值和本地相同key的值相加
List<Double> tempCubeValue= (List<Double>) SerializeUtil.unserialize(value);
for(int j=0;j<cubeValue.size();j++){
cubeValue.set(j,cubeValue.get(j).doubleValue()+tempCubeValue.get(j).doubleValue());
}
}else{
//value为空,说明CacheKey是新计算出来的
newcechekeys.add(newcechekeyMap.get(i));
}
rediskeyvalues.add(rediskeys.get(i));
rediskeyvalues.add(SerializeUtil.serialize(cubeValue));
}
this.jedisDao.mset(rediskeyvalues.toArray(new byte[rediskeyvalues.size()][]));
rediskeys.clear();
rediskeyvalues.clear();
//新生成的cachekey提交sortdb保存
if(newcechekeys.size()>=ReportConstants.cache_batch_size||!it.hasNext()){
sort.openWriteMode();
sort.writeCacheKey(newcechekeys);
newcechekeys.clear();
}
newcechekeyMap.clear();
}
}
}catch(ReportException e){
sort.close();
throw e;
}catch(Exception e){
sort.close();
throw new ReportException(e);
}
}
/**
* 使用sort_db排序key并从redis的cube缓存中提取计算结果
*/
class RedisDR implements DataReader{
private int row_idx=-1;
private int row_max=0;
private List<RedisCacheKey> redisCacheKeys=new ArrayList<RedisCacheKey>();
public void close() throws ReportException {
sort.close();
}
public Object getObject(int i) throws ReportException {
return redisCacheKeys.get(this.row_idx).getKeys().get(i-1);
}
public String getString(int i) throws ReportException {
return redisCacheKeys.get(this.row_idx).getKeys().get(i-1).toString();
}
public boolean next() throws ReportException {
this.row_idx++;
if(row_idx>=this.row_max){
redisCacheKeys.clear();
List<byte[]> rediskeys=new ArrayList<byte[]>(ReportConstants.cache_batch_size);
Map<Integer,RedisCacheKey> cachekeyMap=new HashMap<Integer,RedisCacheKey>(ReportConstants.cache_batch_size);
for(int i=0;i<ReportConstants.cache_batch_size;i++){
//从sortDB读取排序好的RedisCacheKey
RedisCacheKey key= sort.readCacheKey();
if(key==null){ break;}
rediskeys.add(key.getRediskey().getBytes());
cachekeyMap.put(i, key);
}
if(rediskeys.size()>0){
//从redis缓存中提取计算结果
byte[][] bytekeys=rediskeys.toArray(new byte[rediskeys.size()][]);
List<byte[]> values=jedisDao.mget(bytekeys);
for(int i=0;i<values.size();i++ ){
RedisCacheKey key=cachekeyMap.get(i);
key.getKeys().addAll((ArrayList<Double>)SerializeUtil.unserialize(values.get(i)));
redisCacheKeys.add(key);
}
//删除已读取的缓存计算结果数据
jedisDao.del(bytekeys);
}
row_max=redisCacheKeys.size();
this.row_idx=0;
}
if(row_max<=0)
return false;
else
return true;
}
public void open() throws ReportException {
sort.openReadMode();
row_idx=-1;
row_max=0;
}
}
}