記憶體分頁的幾種方法
阿新 • • 發佈:2019-02-13
目錄
第一種:直接取分頁資料
/**
* 處理記憶體分頁
* @param list
* @param pageNo
* @param pageSize
* @return
*/
private <T> List<T> getPageConfigList(List<T> list, int pageNo, int pageSize){
List<T> currentPagelist = null;
if (list != null && !list.isEmpty()) {
currentPagelist = new ArrayList<T>();
int iEnd = 0;
int iStart = 0;
iStart = pageSize * (pageNo - 1);
iEnd = iStart + pageSize;
if (iEnd > list.size()) {
iEnd = list.size();
}
for (int i = iStart; i < iEnd; i++) {
currentPagelist.add(list.get(i));
}
}
return currentPagelist;
}
第二種:先分頁,再取分頁資料
com.google.common.collect.List實現
CollectionContentUtils.sortList(sList, new String[] {"rtn_urlpathmethod"}, "desc");
List<List<TipConfig>> partition = Lists.partition(sList, length);
page.setData(partition.get((start+length-1)/length));
擴充套件知識
guava使用Lists.partition,Lists.transform小結
有時候我們會遇到分割List,把list分成幾份,或者把list的元素轉換成另一個型別的元素,使用 guava的Lists.partition,Lists.transform可以幫忙我們更加簡單的實現此功能
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) throws Exception {
List<Long> list = new ArrayList<>();
list.add(1L);
list.add(2L);
list.add(3L);
list.add(4L);
list.add(5L);
list.add(6L);
list.add(7L);
list.add(8L);
list.add(9L);
List<List<Long>> originalPageList = Lists.partition(list, 3);
List<String> pageList = Lists.transform(originalPageList, new Function<List<Long>, String>() {
@Override
public String apply(List<Long> list) {
final StringBuffer pageSkuIds = new StringBuffer();
for(Long info : list) {
pageSkuIds.append("AA_").append(info).append(",");
}
return pageSkuIds.toString();
}
});
System.out.println(pageList.toString());
}
}
第三種:遍歷的同時,進行分頁
一次遍歷,邊分頁邊批量插入資料庫 ;適合海量資料分批處理。
@DataSource(value = "master")
public void recordRanking() {
logger.error("recordRanking start====================================");
String distributedLockKey = "";//分散式鎖key
String rankingSizeKey = "";//某個活動的排行榜大小key
String batchInsertErrorKey = "";//某個活動批次插入報錯key
String promotionAndRuleKey = "";//時光機活動規則key
try {
// 獲取最近一條需要發放大獎的活動(活動結束即可)
promotionAndRuleKey = "promotionAndRule_promotionType_" + SnsPromotionType.TIMEMACHINE_GAME.getCode();
SnsPromotionShareRuleVo spsrVo = (SnsPromotionShareRuleVo) CacheUtil.getObject(promotionAndRuleKey);
if (spsrVo == null) {
spsrVo = snsPromotionDAO.selectPrixPromotionAndRuleByType(SnsPromotionType.TIMEMACHINE_GAME.getCode());
CacheUtil.setObject(promotionAndRuleKey, spsrVo, 30);
if (spsrVo == null) {
logger.error("沒有已結束需要發放大獎的時光機活動");
return;
}
}
Long promotionId = spsrVo.getPromotionId();
distributedLockKey = String.format("distributedLockKey_recordRanking_promotionId_%d", promotionId);
rankingSizeKey = String.format("rankingSizeKey_recordRanking_promotionId_%d", promotionId);
batchInsertErrorKey = String.format("batchInsertErrorKey_recordRanking_promotionId_%d", promotionId);
// 分散式鎖(保證下面邏輯不會在同一時間重複執行)
if(!CacheUtil.set(distributedLockKey, distributedLockKey, 60*15)){
Object[] args = {distributedLockKey, promotionId};
logger.error("未獲取到分散式鎖,key:{},活動id:{}", args);
return;
}
// 先檢視排行榜表中是否已經全部寫入本次活動的資料
SnsShareRecordRankingExample example = new SnsShareRecordRankingExample();
example.createCriteria().andPromotionIdEqualTo(promotionId);
int count = snsShareRecordRankingDAO.countByExample(example);
String rankingSizeStr = CacheUtil.get(rankingSizeKey);
if (StringUtils.isNotBlank(rankingSizeStr) && Integer.valueOf(rankingSizeStr) <= count) {
logger.error("活動id:" + promotionId + "的排行榜資料已經全部寫入臨時表,排行榜數量:" + count + ",請勿重複操作");
return;
}
// 處理插入失敗的資料
boolean handleFlag = handleBatchInsertErrorList(batchInsertErrorKey, promotionId);
if (handleFlag) {
return;
}
// 獲取排行榜
StopWatch sw1 = new StopWatch();
sw1.start();
List<SnsShareRecord> ssrList = snsShareRecordDAO.getListOfAccomplishedAndFilteredByPromotionId(promotionId);
sw1.stop();
logger.error("snsShareRecordDAO.getListOfAccomplishedAndFilteredByPromotionId耗時:" + sw1.getTotalTimeMillis() + "ms");
if (CollectionUtils.isEmpty(ssrList)) {
logger.error("該時光機活動排行榜為空,活動id:" + promotionId);
return;
}
int rankingSize = ssrList.size();
// 將排行榜大小放入快取
CacheUtil.set(rankingSizeKey, String.valueOf(rankingSize));
logger.error("活動id:" + promotionId + ",排行榜數量:" + rankingSize);
// 分批次寫入
StopWatch sw2 = new StopWatch();
sw2.start();
// n:計數器,m:頁數,l:模數,maxSize:每批處理數量
int n = 0, m = 0, size = rankingSize, maxSize = 500, l = size%maxSize;
int flag = (l == 0) ? 0 : 1;
size = size/maxSize;
List<SnsShareRecordRanking> ssrrListTemp = new ArrayList<>(maxSize);
SnsShareRecordRanking ssrrTemp = null;
for (int i = 0; i < rankingSize; i++) {
SnsShareRecord ssr = ssrList.get(i);
ssrrTemp = new SnsShareRecordRanking();
ssrrTemp.setShareRecordId(ssr.getId());
ssrrTemp.setPromotionId(ssr.getPromotionId());
ssrrTemp.setUserId(ssr.getUserId());
ssrrTemp.setPrixResultState(ssr.getPrixResultState());
ssrrTemp.setTotalRegisterDuration(ssr.getTotalRegisterDuration());
ssrrTemp.setRanking(i+1);
ssrrListTemp.add(ssrrTemp);
n++;
//預處理資料不能整除最大行數時,最後幾行資料的處理,n==l表示最後一部分資料;
if(size == m && n == l) {
flag = 0;
}
/**
* case1:大於等於最大行數
* case2:預處理資料不能整除最大行數時,最後幾行資料的處理,flag=0
* case3:預處理資料量可以整除最大行數,flag=0
*/
if(n >= maxSize || flag == 0) {
try {
snsShareRecordRankingDAO.batchInsert(ssrrListTemp);
Object[] args = {m, ssrrListTemp.size(), rankingSize};
logger.error("snsShareRecordRankingDAO.batchInsert 第{}次寫入成功,本次寫入資料量:{},總資料量:{}", args);
} catch (Exception e) {
Object[] args = {m, ssrrListTemp.size(), rankingSize};
logger.error("snsShareRecordRankingDAO.batchInsert 第{}次寫入失敗,本次寫入資料量:{},總資料量:{}", args);
logger.error("snsShareRecordRankingDAO.batchInsert has error", e.getMessage());
// 記錄該批次資料到快取,後面重新插入
String field = String.format("promotionId_%d_page_%d", promotionId, m);
Integer rankingMix = ssrrListTemp.get(0).getRanking();
Integer rankingMax = ssrrListTemp.get(ssrrListTemp.size()-1).getRanking();
String range = String.format("%d_%d", rankingMix, rankingMax);
CacheUtil.hset(batchInsertErrorKey, field, range);
} finally {
ssrrListTemp.clear();
n = 0;
m++;
}
}
}
sw2.stop();
logger.error("分批次寫入排行榜資料總耗時:" + sw2.getTotalTimeMillis() + "ms");
} catch (Exception e) {
logger.error("recordRanking has error", e.getMessage());
} finally {
// 釋放分散式鎖
CacheUtil.del(distributedLockKey);
logger.error("recordRanking end====================================");
}
}
/**
* 處理批次插入失敗的資料
* @param batchInsertErrorKey
* @param promotionId
* @return
*/
private boolean handleBatchInsertErrorList(String batchInsertErrorKey, Long promotionId) {
logger.error("handleBatchInsertErrorList start==========batchInsertErrorKey:" + batchInsertErrorKey);
boolean flag = false;
StopWatch sw1 = new StopWatch();
sw1.start();
Map<String, String> batchInsertErrorMap = CacheUtil.hgetAll(batchInsertErrorKey);
if (MapUtils.isNotEmpty(batchInsertErrorMap)) {
// 將批量插入失敗的資料重新插入一次
for (Map.Entry<String, String> entry : batchInsertErrorMap.entrySet()) {
String field = entry.getKey();
String range = entry.getValue();
String[] rangeArr = StringUtils.split(range, "_");
Integer rankingMix = Integer.valueOf(StringUtils.replace(rangeArr[0], "\"", ""));
Integer rankingMax = Integer.valueOf(StringUtils.replace(rangeArr[1], "\"", ""));
// 獲取排名範圍內的排行榜資料
List<SnsShareRecord> ssrList = snsShareRecordDAO.getListOfAccomplishedAndFilteredByPromotionIdAndRange(promotionId, rankingMix-1, rankingMax - rankingMix + 1);
if (CollectionUtils.isEmpty(ssrList)) {
Object[] args1 = {field};
logger.error("handleBatchInsertErrorList batchInsertErrorMap中field:{}沒有獲取到該範圍排行榜資料,下面將該field從快取刪除", args1);
CacheUtil.hdel(batchInsertErrorKey, field);
continue;
}
int size = ssrList.size();
Object[] args2 = {field, size};
logger.error("handleBatchInsertErrorList batchInsertErrorMap中field:{}對應的資料量:{},下面將進行批量插入", args2);
List<SnsShareRecordRanking> ssrrListTemp = new ArrayList<>(size);
SnsShareRecordRanking ssrrTemp = null;
int ranking = rankingMix.intValue();//最小排名
for (int i = 0; i < size; i++) {
SnsShareRecord ssr = ssrList.get(i);
ssrrTemp = new SnsShareRecordRanking();
ssrrTemp.setShareRecordId(ssr.getId());
ssrrTemp.setPromotionId(ssr.getPromotionId());
ssrrTemp.setUserId(ssr.getUserId());
ssrrTemp.setPrixResultState(ssr.getPrixResultState());
ssrrTemp.setTotalRegisterDuration(ssr.getTotalRegisterDuration());
ssrrTemp.setRanking(ranking + i);
ssrrListTemp.add(ssrrTemp);
}
// 重新執行批量插入
try {
snsShareRecordRankingDAO.batchInsert(ssrrListTemp);
// 插入成功之後刪除該批次失敗標記
CacheUtil.hdel(batchInsertErrorKey, field);
Object[] args3 = {size};
logger.error("handleBatchInsertErrorList.batchInsert 寫入成功,本次寫入資料量:{}", args3);
} catch (Exception e) {
Object[] args4 = {size};
logger.error("handleBatchInsertErrorList.batchInsert 寫入失敗,本次寫入資料量:{}", args4);
logger.error("handleBatchInsertErrorList.batchInsert has error", e.getMessage());
} finally {
ssrrListTemp.clear();
}
}
flag = true;
}
sw1.stop();
logger.error("handleBatchInsertErrorList end==========batchInsertErrorKey:" + batchInsertErrorKey + ",flag is " + flag + ",耗時:" + sw1.getTotalTimeMillis() + "ms");
return flag;
}