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

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

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

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

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

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

這是最簡單易懂的方案,我們按照不同的分頁條件查詢出結(jié)果后,直接緩存分頁結(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)是工程簡單,性能也快,但是有一個明顯的缺陷基因:列表緩存的顆粒度非常大 。

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

有兩種方式 :

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

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

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

2 查詢對象ID列表,再緩存每個對象條目

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

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

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

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

偽代碼如下:

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

核心流程:

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

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

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

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

2、批量從緩存中獲取商品對象

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ù)庫查詢未命中的商品信息列表,重新加載到緩存

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

ListnoHitProductList=batchQuery(noHitIdList);

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

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

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

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

5、 遍歷商品ID列表,組裝對象列表

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

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

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

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

下圖是開源中國的搜索流程:

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

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

3 緩存對象ID列表,同時緩存每個對象條目

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

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

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

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

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

score : 分值,存儲類型是 double

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

如上圖所示:ZSet 存儲動態(tài) ID 列表 , member 的值是動態(tài)編號 , score 值是創(chuàng)建時間

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

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

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

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

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

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

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

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

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

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

我們再來模擬獲取動態(tài)分頁列表的流程:

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

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

4 總結(jié)

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

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

查詢對象ID列表,只緩存每個對象條目

緩存對象ID列表,同時緩存每個對象條目

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





審核編輯:劉清

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

    關(guān)注

    38

    文章

    7438

    瀏覽量

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

    關(guān)注

    1

    文章

    753

    瀏覽量

    44036
  • MYSQL數(shù)據(jù)庫
    +關(guān)注

    關(guān)注

    0

    文章

    95

    瀏覽量

    9375
  • Redis
    +關(guān)注

    關(guān)注

    0

    文章

    370

    瀏覽量

    10830

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

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

收藏 人收藏

    評論

    相關(guān)推薦

    多列列表分頁顯示

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

    mysql分頁

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

    JPA分頁查詢的常用方法

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

    請問mybatis分頁插件有哪些使用案列?

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

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

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

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

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

    Jquery簡單分頁實(shí)現(xiàn)

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

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

    片頭因?yàn)榄h(huán)形緩存在單片機(jī)程序中的使用是非常有效的,非常有用的,關(guān)于這個話題在此專門開一文章來聊聊這個話題。環(huán)形緩存的用途主要是來緩存數(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百萬數(shù)據(jù)的深分頁問題

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

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

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

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

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

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

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

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

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

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

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

    聊聊緩存擊穿的解決方法

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