java數(shù)據(jù)隨機分頁實現(xiàn)方案
導語 | 網(wǎng)上搜到的方法,是使用數(shù)據(jù)庫的隨機排序ORDER BY RAND()進行的,較大數(shù)據(jù)的時候,顯然就不好使了,而且在數(shù)據(jù)庫層面進行隨機分頁就比較困難,無法保證基礎的有序性,因此需要考慮其他方法來進行實現(xiàn):數(shù)據(jù)庫+redis+List洗牌的方式就孕育而生。
問題產(chǎn)生
公司業(yè)務遇到此場景:在前端分頁展示數(shù)據(jù)時,為了讓每個數(shù)據(jù)都有相同的幾率被展示,每次分頁展示數(shù)據(jù)時,都是隨機展示的,并要求每一頁之間數(shù)據(jù)不能重復;而且,優(yōu)先展示最近三天的上傳的數(shù)據(jù),用戶看完數(shù)據(jù)后,繼續(xù)加載三天前又三天的數(shù)據(jù)。
此時大概率會有一會有以下痛點:
- 有用戶一直傳數(shù)據(jù),就會出現(xiàn)這個用戶數(shù)據(jù)占滿一頁;
- 越晚上傳的數(shù)據(jù)曝光量會越好,越早上傳的數(shù)據(jù)曝光量越差;
- 類似這種3天又3天數(shù)據(jù)如何加載?;
因此提出這個需求;數(shù)據(jù)需要隨機分頁出現(xiàn)。
問題分析與解決
分析一
遇到這種問題,單純的使用數(shù)據(jù)庫就不好使了,況且數(shù)據(jù)庫本身就比較脆弱;因此,我想到引入第三方工具:redis,而其我們軟件本身就使用redis;使用redis性能也會得到極大提升,主要用到redis list 的方法;
分析二
針對問題一,同一用戶數(shù)據(jù)占滿一頁,以及問題二的解決辦法;大腦跳出來的方法就是數(shù)據(jù)隨機選擇,這時我想到通過list洗牌的方法。
分析三
問題三,用戶看完數(shù)據(jù)后繼續(xù)加載后面的數(shù)據(jù),類似【懶加載】;解決辦法:當用戶刷數(shù)據(jù)到最后一頁的時候,就觸發(fā)【懶加載】,數(shù)據(jù)追加到redis list里面。
實現(xiàn)步驟與部分代碼
1. 查詢?nèi)斓臄?shù)據(jù),把主鍵id list 洗牌放入redis list;
// 使用 package java.util.Collections;
/**
* Randomly permutes the specified list using a default source of
* randomness. All permutations occur with approximately equal
* likelihood.
*/
public static void shuffle(List<?> list) {
Random rnd = r;
if (rnd == null)
r = rnd = new Random(); // harmless race.
shuffle(list, rnd);
} /**
* 追加Lisit
*
* @param key 關鍵字
* @param list 列表
* @param expireTime 有效期
* @return boolean
*/
public Boolean addAll(String key, List list, Long expireTime) {
try {
redisTemplate.opsForList().rightPushAll(key, list);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
return true;
} catch (JedisException e) {
handleJedisException(e);
throw e;
} finally {
RedisConnectionUtils.unbindConnection(Objects.requireNonNull(redisTemplate.getConnectionFactory()));
}
}這里需要注意,設置key的有效期;否則,你懂的?。?!
2. 觸發(fā)數(shù)據(jù)【懶加載】
/**
* 構(gòu)建分頁數(shù)據(jù)
* @param so 入?yún)?
* @param key redis key
* @param dayKey 天數(shù)區(qū)間的key
* @return page
*/
private IPage<PictureAlbumForUserDTO> buildPage(PublishedPictureAlbumSO so, String key, String dayKey) {
Long total = redisUtil.listSize(key);
long toIndex = so.getSize() * so.getPage();
if (toIndex > total) { // 用戶加載到最后一頁,觸發(fā)【懶加載】
// 發(fā)現(xiàn)池,加載更多數(shù)據(jù)
String dayValue = redisUtil.get(dayKey);
String[] dayValueArray = dayValue.split("#");
long start = Long.parseLong(dayValueArray[0]) - rangeDay;
long end = Long.parseLong(dayValueArray[1]) - rangeDay;
if (appendList(key, dayKey, start, end)) {
// 遞歸調(diào)用;遞歸后,數(shù)據(jù)追加到redis
return buildPage(so, key, dayKey);
} else {
toIndex = total;
}
}
// 根據(jù)下標獲取redis里面分頁數(shù)據(jù)
List list = redisUtil.listRange(key, so.getSize() * (so.getPage() - 1), toIndex);
if (CollectionUtil.isEmpty(list)) {
return null;
}
so.setAlbumIdList(list);
// 因為進行l(wèi)ist id 分頁,這里默認去數(shù)據(jù)庫里查詢第一頁就行了
so.setPage(1);
Page<PictureAlbumForUserDTO> pageData = new Page<>(so.getPage(), so.getSize());
IPage<PictureAlbumForUserDTO> iPage = baseMapper.findAllActive(pageData, so);
List<PictureAlbumForUserDTO> dtoList = iPage.getRecords();
Collections.shuffle(dtoList);
iPage.setRecords(dtoList);
iPage.setTotal(total);
iPage.setSize(so.getSize());
iPage.setCurrent(so.getPage());
iPage.setPages((total + so.getSize() - 1) / so.getSize());
return iPage;
} /**
* 讀取redis lisit 區(qū)間數(shù)據(jù)
*
* @param key 關鍵字
* @param start 開始下標
* @param end 結(jié)束下標
* @return boolean
*/
public List listRange(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (JedisException e) {
handleJedisException(e);
throw e;
} finally {
RedisConnectionUtils.unbindConnection(Objects.requireNonNull(redisTemplate.getConnectionFactory()));
}
}3. 每頁數(shù)據(jù)隨機展示
如何想讓用戶每一頁看到的數(shù)據(jù)不一樣;分頁的數(shù)據(jù)可以再進行洗牌,而且不會打亂數(shù)據(jù)池整體的數(shù)據(jù)排列,有種【千人千面】的錯覺;缺點就是用戶可能會覺得,你的分頁數(shù)據(jù)存在問題。
總結(jié)
介紹了一種實現(xiàn)隨機分頁的方案,關鍵在與數(shù)據(jù)庫結(jié)合redis的使用和list的洗牌,這種方法從效率和使用性性都比較高的;對付萬級核心數(shù)據(jù)的隨機分頁,應該是沒有問題的。
到此這篇關于java數(shù)據(jù)隨機分頁實現(xiàn)方案的文章就介紹到這了,更多相關java數(shù)據(jù)隨機分頁內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
在idea2023中使用SpringBoot整合Lombok全過程及詳細用法
Lombok項目是一個java庫,它可以自動插入到編輯器和構(gòu)建工具中,增強java的性能,本文詳細給大家介紹了在idea2023中使用SpringBoot整合Lombok全過程及詳細用法,需要的朋友可以參考下2023-09-09
SpringBoot整合Elasticsearch實現(xiàn)索引和文檔的操作方法
Elasticsearch 基于 Apache Lucene 構(gòu)建,采用 Java 編寫,并使用 Lucene 構(gòu)建索引、提供搜索功能,本文分步驟通過綜合案例給大家分享SpringBoot整合Elasticsearch的相關知識,感興趣的朋友跟隨小編一起看看吧2021-05-05
Spring?Boot中使用Spring?Retry重試框架的操作方法
這篇文章主要介紹了Spring?Retry?在SpringBoot?中的應用,介紹了RetryTemplate配置的時候,需要設置的重試策略和退避策略,需要的朋友可以參考下2022-04-04
Spring Boot高級教程之使用Redis實現(xiàn)session共享
這篇文章主要為大家詳細介紹了Spring Boot高級教程之使用Redis實現(xiàn)session共享,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-10-10
Java中Stream流中map和forEach的區(qū)別詳解
本文主要介紹了Java中Stream流中map和forEach的區(qū)別詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-04-04
Java?C++題解leetcode字符串輪轉(zhuǎn)KMP算法詳解
這篇文章主要為大家介紹了Java?C++題解leetcode字符串輪轉(zhuǎn)KMP算法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09

