java數(shù)據(jù)隨機(jī)分頁實(shí)現(xiàn)方案
導(dǎo)語 | 網(wǎng)上搜到的方法,是使用數(shù)據(jù)庫的隨機(jī)排序ORDER BY RAND()進(jìn)行的,較大數(shù)據(jù)的時(shí)候,顯然就不好使了,而且在數(shù)據(jù)庫層面進(jìn)行隨機(jī)分頁就比較困難,無法保證基礎(chǔ)的有序性,因此需要考慮其他方法來進(jìn)行實(shí)現(xiàn):數(shù)據(jù)庫+redis+List洗牌的方式就孕育而生。
問題產(chǎn)生
公司業(yè)務(wù)遇到此場景:在前端分頁展示數(shù)據(jù)時(shí),為了讓每個(gè)數(shù)據(jù)都有相同的幾率被展示,每次分頁展示數(shù)據(jù)時(shí),都是隨機(jī)展示的,并要求每一頁之間數(shù)據(jù)不能重復(fù);而且,優(yōu)先展示最近三天的上傳的數(shù)據(jù),用戶看完數(shù)據(jù)后,繼續(xù)加載三天前又三天的數(shù)據(jù)。
此時(shí)大概率會(huì)有一會(huì)有以下痛點(diǎn):
- 有用戶一直傳數(shù)據(jù),就會(huì)出現(xiàn)這個(gè)用戶數(shù)據(jù)占滿一頁;
- 越晚上傳的數(shù)據(jù)曝光量會(huì)越好,越早上傳的數(shù)據(jù)曝光量越差;
- 類似這種3天又3天數(shù)據(jù)如何加載?;
因此提出這個(gè)需求;數(shù)據(jù)需要隨機(jī)分頁出現(xiàn)。
問題分析與解決
分析一
遇到這種問題,單純的使用數(shù)據(jù)庫就不好使了,況且數(shù)據(jù)庫本身就比較脆弱;因此,我想到引入第三方工具:redis,而其我們軟件本身就使用redis;使用redis性能也會(huì)得到極大提升,主要用到redis list 的方法;
分析二
針對(duì)問題一,同一用戶數(shù)據(jù)占滿一頁,以及問題二的解決辦法;大腦跳出來的方法就是數(shù)據(jù)隨機(jī)選擇,這時(shí)我想到通過list洗牌的方法。
分析三
問題三,用戶看完數(shù)據(jù)后繼續(xù)加載后面的數(shù)據(jù),類似【懶加載】;解決辦法:當(dāng)用戶刷數(shù)據(jù)到最后一頁的時(shí)候,就觸發(fā)【懶加載】,數(shù)據(jù)追加到redis list里面。
實(shí)現(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 關(guān)鍵字 * @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())); } }
這里需要注意,設(shè)置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ù)下標(biāo)獲取redis里面分頁數(shù)據(jù) List list = redisUtil.listRange(key, so.getSize() * (so.getPage() - 1), toIndex); if (CollectionUtil.isEmpty(list)) { return null; } so.setAlbumIdList(list); // 因?yàn)檫M(jìn)行l(wèi)ist id 分頁,這里默認(rèn)去數(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 關(guān)鍵字 * @param start 開始下標(biāo) * @param end 結(jié)束下標(biāo) * @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ù)隨機(jī)展示
如何想讓用戶每一頁看到的數(shù)據(jù)不一樣;分頁的數(shù)據(jù)可以再進(jìn)行洗牌,而且不會(huì)打亂數(shù)據(jù)池整體的數(shù)據(jù)排列,有種【千人千面】的錯(cuò)覺;缺點(diǎn)就是用戶可能會(huì)覺得,你的分頁數(shù)據(jù)存在問題。
總結(jié)
介紹了一種實(shí)現(xiàn)隨機(jī)分頁的方案,關(guān)鍵在與數(shù)據(jù)庫結(jié)合redis的使用和list的洗牌,這種方法從效率和使用性性都比較高的;對(duì)付萬級(jí)核心數(shù)據(jù)的隨機(jī)分頁,應(yīng)該是沒有問題的。
到此這篇關(guān)于java數(shù)據(jù)隨機(jī)分頁實(shí)現(xiàn)方案的文章就介紹到這了,更多相關(guān)java數(shù)據(jù)隨機(jī)分頁內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在idea2023中使用SpringBoot整合Lombok全過程及詳細(xì)用法
Lombok項(xiàng)目是一個(gè)java庫,它可以自動(dòng)插入到編輯器和構(gòu)建工具中,增強(qiáng)java的性能,本文詳細(xì)給大家介紹了在idea2023中使用SpringBoot整合Lombok全過程及詳細(xì)用法,需要的朋友可以參考下2023-09-09SpringBoot整合Elasticsearch實(shí)現(xiàn)索引和文檔的操作方法
Elasticsearch 基于 Apache Lucene 構(gòu)建,采用 Java 編寫,并使用 Lucene 構(gòu)建索引、提供搜索功能,本文分步驟通過綜合案例給大家分享SpringBoot整合Elasticsearch的相關(guān)知識(shí),感興趣的朋友跟隨小編一起看看吧2021-05-05Spring?Boot中使用Spring?Retry重試框架的操作方法
這篇文章主要介紹了Spring?Retry?在SpringBoot?中的應(yīng)用,介紹了RetryTemplate配置的時(shí)候,需要設(shè)置的重試策略和退避策略,需要的朋友可以參考下2022-04-04Spring Boot高級(jí)教程之使用Redis實(shí)現(xiàn)session共享
這篇文章主要為大家詳細(xì)介紹了Spring Boot高級(jí)教程之使用Redis實(shí)現(xiàn)session共享,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10Java中Stream流中map和forEach的區(qū)別詳解
本文主要介紹了Java中Stream流中map和forEach的區(qū)別詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04Java?C++題解leetcode字符串輪轉(zhuǎn)KMP算法詳解
這篇文章主要為大家介紹了Java?C++題解leetcode字符串輪轉(zhuǎn)KMP算法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09