0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

聊聊分頁(yè)列表緩存設(shè)計(jì)

jf_ro2CN3Fa ? 來(lái)源:勇哥java實(shí)戰(zhàn)分享 ? 2023-06-06 18:25 ? 次閱讀

1 直接緩存分頁(yè)列表結(jié)果

這是最簡(jiǎn)單易懂的方案,我們按照不同的分頁(yè)條件查詢出結(jié)果后,直接緩存分頁(yè)結(jié)果 。

c38fc192-043c-11ee-90ce-dac502259ad0.png

偽代碼如下:

publicListgetPageList(Stringparam,intpage,intsize){
Stringkey="productList"+page+"size:"+size+
"param:"+param;
ListdataList=cacheUtils.get(key);
if(dataList!=null){
returndataList;
}
dataList=queryFromDataBase(param,page,size);
if(dataList!=null){
cacheUtils.set(key,dataList,Constants.ExpireTime);
}
}

這種方案的優(yōu)點(diǎn)是工程簡(jiǎn)單,性能也快,但是有一個(gè)明顯的缺陷基因:列表緩存的顆粒度非常大 。

假如列表中數(shù)據(jù)發(fā)生增刪,為了保證數(shù)據(jù)的一致性,需要修改分頁(yè)列表緩存。

有兩種方式 :

1、依靠緩存過期來(lái)惰性的實(shí)現(xiàn) ,但業(yè)務(wù)場(chǎng)景必須包容;

2、使用 Redis 的 keys 找到該業(yè)務(wù)的分頁(yè)緩存,執(zhí)行刪除指令。但 keys 命令對(duì)性能影響很大,會(huì)導(dǎo)致 Redis 很大的延遲 。

生產(chǎn)環(huán)境使用 keys 命令比較危險(xiǎn),發(fā)生事故的幾率高,非常不推薦使用

2 查詢對(duì)象ID列表,再緩存每個(gè)對(duì)象條目

直接緩存分頁(yè)結(jié)果雖然好用,但緩存的顆粒度太大,保證數(shù)據(jù)一致性比較麻煩。

所以我們的目標(biāo)是更細(xì)粒度的控制緩存

c3b84ca2-043c-11ee-90ce-dac502259ad0.png

我們先查詢出商品分頁(yè)對(duì)象ID列表,然后為每一個(gè)商品對(duì)象創(chuàng)建緩存 , 通過商品ID和商品對(duì)象緩存聚合成列表返回給前端。

偽代碼如下:

c3cb0982-043c-11ee-90ce-dac502259ad0.png

核心流程:

1、從數(shù)據(jù)庫(kù)中查詢分頁(yè) ID 列表

//從數(shù)據(jù)庫(kù)中查詢分頁(yè)商品ID列表
ListproductIdList=queryProductIdListFromDabaBase(
param,
page,
size);

對(duì)應(yīng)的 SQL 類似:

SELECTidFROMproducts
ORDERBYidASC
LIMIT(page-1)*size,size

2、批量從緩存中獲取商品對(duì)象

MapcachedProductMap=cacheUtils.mget(productIdList);

假如我們使用本地緩存,直接一條一條從本地緩存中聚合也極快。

假如我們使用分布式緩存,Redis 天然支持批量查詢的命令 ,比如 mget ,hmget 。

3、組裝沒有命中的商品ID

ListnoHitIdList=newArrayList<>(cachedProductMap.size());
for(LongproductId:productIdList){
if(!cachedProductMap.containsKey(productId)){
noHitIdList.add(productId);
}
}

因?yàn)榫彺嬷锌赡芤驗(yàn)檫^期或者其他原因?qū)е戮彺鏇]有命中的情況,所以我們需要找到哪些商品沒有在緩存里。

4、批量從數(shù)據(jù)庫(kù)查詢未命中的商品信息列表,重新加載到緩存

首先從數(shù)據(jù)庫(kù)里批量 查詢出未命中的商品信息列表 ,請(qǐng)注意是批量 。

ListnoHitProductList=batchQuery(noHitIdList);

參數(shù)是未命中緩存的商品ID列表,組裝成對(duì)應(yīng)的 SQL,這樣性能更快 :

SELECT*FROMproductsWHEREidIN
(1,
2,
3,
4);

然后這些未命中的商品信息存儲(chǔ)到緩存里 , 使用 Redis 的 mset 命令。

//將沒有命中的商品加入到緩存里
MapnoHitProductMap=
noHitProductList.stream()
.collect(
Collectors.toMap(Product::getId,Function.identity())
);
cacheUtils.mset(noHitProductMap);
//將沒有命中的商品加入到聚合map里
cachedProductMap.putAll(noHitProductMap);

5、 遍歷商品ID列表,組裝對(duì)象列表

for(LongproductId:productIdList){
Productproduct=cachedProductMap.get(productId);
if(product!=null){
result.add(product);
}
}

當(dāng)前方案里,緩存都有命中的情況下,經(jīng)過兩次網(wǎng)絡(luò) IO ,第一次數(shù)據(jù)庫(kù)查詢 IO ,第二次 Redis 查詢 IO , 性能都會(huì)比較好。

所有的操作都是批量操作,就算有緩存沒有命中的情況,整體速度也較快。

查詢對(duì)象ID列表,再緩存每個(gè)對(duì)象條目 “ 這個(gè)方案比較靈活,當(dāng)我們查詢對(duì)象ID列表 ,可以不限于數(shù)據(jù)庫(kù),還可以是搜索引擎,Redis 等等。

下圖是開源中國(guó)的搜索流程:

c42fe136-043c-11ee-90ce-dac502259ad0.png

精髓在于:搜索的分頁(yè)結(jié)果只包含業(yè)務(wù)對(duì)象 ID ,對(duì)象的詳細(xì)資料需要從緩存 + MySQL 中獲取。

3 緩存對(duì)象ID列表,同時(shí)緩存每個(gè)對(duì)象條目

筆者曾經(jīng)重構(gòu)過類似朋友圈的服務(wù),進(jìn)入班級(jí)頁(yè)面 ,瀑布流的形式展示班級(jí)成員的所有動(dòng)態(tài)。

c443a2e8-043c-11ee-90ce-dac502259ad0.png

我們使用推模式將每一條動(dòng)態(tài) ID 存儲(chǔ)在 Redis ZSet 數(shù)據(jù)結(jié)構(gòu)中 。Redis ZSet 是一種類型為有序集合的數(shù)據(jù)結(jié)構(gòu),它由多個(gè)有序的唯一的字符串元素組成,每個(gè)元素都關(guān)聯(lián)著一個(gè)浮點(diǎn)數(shù)分值。

ZSet 使用的是 member -> score 結(jié)構(gòu) :

member : 成員,也是默認(rèn)的第二排序維度( score 相同時(shí),Redis 以 member 的字典序排列)

score : 分值,存儲(chǔ)類型是 double

c46dec4c-043c-11ee-90ce-dac502259ad0.png

如上圖所示:ZSet 存儲(chǔ)動(dòng)態(tài) ID 列表 , member 的值是動(dòng)態(tài)編號(hào) , score 值是創(chuàng)建時(shí)間 。

通過 ZSet 的 ZREVRANGE 命令 就可以實(shí)現(xiàn)分頁(yè)的效果。

ZREVRANGE 是 Redis 中用于有序集合(sorted set)的命令之一,它用于按照成員的分?jǐn)?shù)從大到小返回有序集合中的指定范圍的成員。

c48084d8-043c-11ee-90ce-dac502259ad0.png

為了達(dá)到分頁(yè)的效果,傳遞如下的分頁(yè)參數(shù) :

c496a452-043c-11ee-90ce-dac502259ad0.png

通過 ZREVRANGE 命令,我們可以查詢出動(dòng)態(tài) ID 列表。

查詢出動(dòng)態(tài) ID 列表后,還需要緩存每個(gè)動(dòng)態(tài)對(duì)象條目,動(dòng)態(tài)對(duì)象包含了詳情,評(píng)論,點(diǎn)贊,收藏這些功能數(shù)據(jù) ,我們需要為這些數(shù)據(jù)提供單獨(dú)做緩存配置。

c4af9ff2-043c-11ee-90ce-dac502259ad0.png

無(wú)論是查詢緩存,還是重新寫入緩存,為了提升系統(tǒng)性能,批量操作效率更高。

緩存對(duì)象結(jié)構(gòu)簡(jiǎn)單,使用 mget 、hmget 命令;若結(jié)構(gòu)復(fù)雜,可以考慮使用 pipleline,Lua 腳本模式 。 筆者選擇的批量方案是 Redis 的 pipleline 功能。

我們?cè)賮?lái)模擬獲取動(dòng)態(tài)分頁(yè)列表的流程:

使用 ZSet 的 ZREVRANGE 命令 ,傳入分頁(yè)參數(shù),查詢出動(dòng)態(tài) ID 列表 ;

傳遞動(dòng)態(tài) ID 列表參數(shù),通過 Redis 的 pipleline 功能從緩存中批量獲取動(dòng)態(tài)的詳情,評(píng)論,點(diǎn)贊,收藏這些功能數(shù)據(jù) ,組裝成列表 。

4 總結(jié)

本文介紹了實(shí)現(xiàn)分頁(yè)列表緩存的三種方式:

直接緩存分頁(yè)列表結(jié)果

查詢對(duì)象ID列表,只緩存每個(gè)對(duì)象條目

緩存對(duì)象ID列表,同時(shí)緩存每個(gè)對(duì)象條目

這三種方式是一層一層遞進(jìn)的,要訣是:細(xì)粒度的控制緩存批量加載對(duì)象 。





審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 存儲(chǔ)器
    +關(guān)注

    關(guān)注

    38

    文章

    7403

    瀏覽量

    163395
  • SQL
    SQL
    +關(guān)注

    關(guān)注

    1

    文章

    751

    瀏覽量

    43987
  • MYSQL數(shù)據(jù)庫(kù)

    關(guān)注

    0

    文章

    95

    瀏覽量

    9372
  • Redis
    +關(guān)注

    關(guān)注

    0

    文章

    370

    瀏覽量

    10810

原文標(biāo)題:分頁(yè)列表緩存就該這樣設(shè)計(jì)!

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    多列列表分頁(yè)顯示

    現(xiàn)在希望對(duì)多列列表框做如下操作:從數(shù)據(jù)庫(kù)中輸入10000行數(shù)據(jù)導(dǎo)入多列列表框,要求多列列表框能夠分100頁(yè)顯示,每頁(yè)顯示100條數(shù)據(jù),且能夠利用數(shù)值控件進(jìn)行翻頁(yè)。
    發(fā)表于 11-04 11:15

    mysql分頁(yè)

    第7章 分頁(yè)程序(4課時(shí))
    發(fā)表于 04-30 11:51

    JPA分頁(yè)查詢的常用方法

    JPA分頁(yè)查詢與條件分頁(yè)查詢
    發(fā)表于 10-23 17:10

    請(qǐng)問mybatis分頁(yè)插件有哪些使用案列?

    mybatis分頁(yè)插件使用案例
    發(fā)表于 11-09 06:21

    聊聊環(huán)形緩存在單片機(jī)程序中的使用

    片頭因?yàn)榄h(huán)形緩存在單片機(jī)程序中的使用是非常有效的,非常有用的,關(guān)于這個(gè)話題在此專門開一文章來(lái)聊聊這個(gè)話題。環(huán)形緩存的用途主要是來(lái)緩存數(shù)據(jù),而需要緩存
    發(fā)表于 12-06 08:29

    XML數(shù)據(jù)分頁(yè)索引技術(shù)研究

    對(duì)海量XML文檔的索引查詢技術(shù)進(jìn)行研究,提出一種XML數(shù)據(jù)分頁(yè)索引查詢實(shí)現(xiàn)方法。該方法把頁(yè)面元素標(biāo)記數(shù)量作為數(shù)據(jù)分頁(yè)依據(jù),建立XML數(shù)據(jù)的分頁(yè)索引,并在該分頁(yè)索引上實(shí)現(xiàn)XPath
    發(fā)表于 03-31 10:07 ?10次下載

    Jquery簡(jiǎn)單分頁(yè)實(shí)現(xiàn)

    這篇文章主要介紹了Jquery簡(jiǎn)單分頁(yè)實(shí)現(xiàn)方法,實(shí)例分析了jquery分頁(yè)的相關(guān)實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下。
    發(fā)表于 11-28 11:55 ?1066次閱讀

    串口 單片機(jī) 文件_從環(huán)形緩存到流水緩存在STM32單片機(jī)的應(yīng)用

    片頭因?yàn)榄h(huán)形緩存在單片機(jī)程序中的使用是非常有效的,非常有用的,關(guān)于這個(gè)話題在此專門開一文章來(lái)聊聊這個(gè)話題。環(huán)形緩存的用途主要是來(lái)緩存數(shù)據(jù),而需要緩存
    發(fā)表于 11-23 18:21 ?15次下載
    串口 單片機(jī)  文件_從環(huán)形<b class='flag-5'>緩存</b>到流水<b class='flag-5'>緩存</b>在STM32單片機(jī)的應(yīng)用

    如何優(yōu)化MySQL百萬(wàn)數(shù)據(jù)的深分頁(yè)問題

    我們?nèi)粘W?b class='flag-5'>分頁(yè)需求時(shí),一般會(huì)用limit實(shí)現(xiàn),但是當(dāng)偏移量特別大的時(shí)候,查詢效率就變得低下。本文將分四個(gè)方案,討論如何優(yōu)化MySQL百萬(wàn)數(shù)據(jù)的深分頁(yè)問題,并附上最近優(yōu)化生產(chǎn)慢SQL的實(shí)戰(zhàn)案例。
    的頭像 發(fā)表于 04-06 15:12 ?1837次閱讀

    聊聊緩存數(shù)據(jù)庫(kù)一致性

    在云服務(wù)中,緩存是極其重要的一點(diǎn)。所謂緩存,其實(shí)是一個(gè)高速數(shù)據(jù)存儲(chǔ)層。當(dāng)緩存存在后,日后再次請(qǐng)求該數(shù)據(jù)就會(huì)直接訪問緩存,提升數(shù)據(jù)訪問的速度。
    的頭像 發(fā)表于 01-30 17:41 ?689次閱讀

    圖文詳解Linux分頁(yè)機(jī)制

    分頁(yè)機(jī)制是 80x86 內(nèi)存管理機(jī)制的第二種機(jī)制,分段機(jī)制用于把虛擬地址轉(zhuǎn)換為線性地址,而分頁(yè)機(jī)制用于把線性地址轉(zhuǎn)換為物理地址。
    發(fā)表于 05-30 09:10 ?409次閱讀
    圖文詳解Linux<b class='flag-5'>分頁(yè)</b>機(jī)制

    聊聊本地緩存和分布式緩存

    本地緩存 :應(yīng)用中的緩存組件,緩存組件和應(yīng)用在同一進(jìn)程中,緩存的讀寫非???,沒有網(wǎng)絡(luò)開銷。但各應(yīng)用或集群的各節(jié)點(diǎn)都需要維護(hù)自己的單獨(dú)緩存,無(wú)
    發(fā)表于 06-11 15:12 ?763次閱讀
    <b class='flag-5'>聊聊</b>本地<b class='flag-5'>緩存</b>和分布式<b class='flag-5'>緩存</b>

    聊聊如何實(shí)現(xiàn)一種閃存緩存設(shè)計(jì)

    許多web服務(wù)需要對(duì)數(shù)十億個(gè)小對(duì)象實(shí)現(xiàn)快速訪問,而每個(gè)小對(duì)象只有幾百個(gè)字節(jié)。為了實(shí)現(xiàn)這一點(diǎn)同時(shí)考慮實(shí)際生產(chǎn)效益,緩存系統(tǒng)必須做到同時(shí)低成本,大容量與高性能。
    的頭像 發(fā)表于 08-29 09:01 ?628次閱讀
    <b class='flag-5'>聊聊</b>如何實(shí)現(xiàn)一種閃存<b class='flag-5'>緩存</b>設(shè)計(jì)

    mybatis邏輯分頁(yè)和物理分頁(yè)的區(qū)別

    MyBatis是一個(gè)開源的Java持久層框架,它與其他ORM(對(duì)象關(guān)系映射)框架相比,具有更加靈活和高性能的特點(diǎn)。MyBatis提供了兩種分頁(yè)方式,即邏輯分頁(yè)和物理分頁(yè)。在本文中,我們將詳細(xì)介紹
    的頭像 發(fā)表于 12-03 14:54 ?781次閱讀

    聊聊緩存擊穿的解決方法

    緩存擊穿,Redis中的某個(gè)熱點(diǎn)key不存在或者過期,但是此時(shí)有大量的用戶訪問該key。比如xxx直播間優(yōu)惠券搶購(gòu)、xxx商品活動(dòng),這時(shí)候大量用戶會(huì)在某個(gè)時(shí)間點(diǎn)一同訪問該熱點(diǎn)事件。但是可能
    的頭像 發(fā)表于 10-23 13:54 ?42次閱讀