1 聊聊分页列表缓存设计-德赢Vwin官网 网
0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

聊聊分页列表缓存设计

jf_ro2CN3Fa 来源:勇哥java实战分享 2023-06-06 18:25 次阅读

1 直接缓存分页列表结果

这是最简单易懂的方案,我们按照不同的分页条件查询出结果后,直接缓存分页结果 。

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);
}
}

这种方案的优点是工程简单,性能也快,但是有一个明显的缺陷基因:列表缓存的颗粒度非常大

假如列表中数据发生增删,为了保证数据的一致性,需要修改分页列表缓存。

有两种方式 :

1、依靠缓存过期来惰性的实现 ,但业务场景必须包容;

2、使用 Redis 的 keys 找到该业务的分页缓存,执行删除指令。但 keys 命令对性能影响很大,会导致 Redis 很大的延迟 。

生产环境使用 keys 命令比较危险,发生事故的几率高,非常不推荐使用

2 查询对象ID列表,再缓存每个对象条目

直接缓存分页结果虽然好用,但缓存的颗粒度太大,保证数据一致性比较麻烦。

所以我们的目标是更细粒度的控制缓存

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

我们先查询出商品分页对象ID列表,然后为每一个商品对象创建缓存 , 通过商品ID和商品对象缓存聚合成列表返回给前端。

伪代码如下:

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

核心流程:

1、从数据库中查询分页 ID 列表

//从数据库中查询分页商品ID列表
ListproductIdList=queryProductIdListFromDabaBase(
param,
page,
size);

对应的 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);
}
}

因为缓存中可能因为过期或者其他原因导致缓存没有命中的情况,所以我们需要找到哪些商品没有在缓存里。

4、批量从数据库查询未命中的商品信息列表,重新加载到缓存

首先从数据库里批量 查询出未命中的商品信息列表 ,请注意是批量

ListnoHitProductList=batchQuery(noHitIdList);

参数是未命中缓存的商品ID列表,组装成对应的 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);
}
}

当前方案里,缓存都有命中的情况下,经过两次网络 IO ,第一次数据库查询 IO ,第二次 Redis 查询 IO , 性能都会比较好。

所有的操作都是批量操作,就算有缓存没有命中的情况,整体速度也较快。

查询对象ID列表,再缓存每个对象条目 “ 这个方案比较灵活,当我们查询对象ID列表 ,可以不限于数据库,还可以是搜索引擎,Redis 等等。

下图是开源中国的搜索流程:

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

精髓在于:搜索的分页结果只包含业务对象 ID ,对象的详细资料需要从缓存 + MySQL 中获取。

3 缓存对象ID列表,同时缓存每个对象条目

笔者曾经重构过类似朋友圈的服务,进入班级页面 ,瀑布流的形式展示班级成员的所有动态。

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

我们使用推模式将每一条动态 ID 存储在 Redis ZSet 数据结构中 。Redis ZSet 是一种类型为有序集合的数据结构,它由多个有序的唯一的字符串元素组成,每个元素都关联着一个浮点数分值。

ZSet 使用的是 member -> score 结构 :

member : 成员,也是默认的第二排序维度( score 相同时,Redis 以 member 的字典序排列)

score : 分值,存储类型是 double

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

如上图所示:ZSet 存储动态 ID 列表 , member 的值是动态编号 , score 值是创建时间

通过 ZSet 的 ZREVRANGE 命令 就可以实现分页的效果。

ZREVRANGE 是 Redis 中用于有序集合(sorted set)的命令之一,它用于按照成员的分数从大到小返回有序集合中的指定范围的成员。

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

为了达到分页的效果,传递如下的分页参数 :

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

通过 ZREVRANGE 命令,我们可以查询出动态 ID 列表。

查询出动态 ID 列表后,还需要缓存每个动态对象条目,动态对象包含了详情,评论,点赞,收藏这些功能数据 ,我们需要为这些数据提供单独做缓存配置。

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

无论是查询缓存,还是重新写入缓存,为了提升系统性能,批量操作效率更高。

缓存对象结构简单,使用 mget 、hmget 命令;若结构复杂,可以考虑使用 pipleline,Lua 脚本模式 。 笔者选择的批量方案是 Redis 的 pipleline 功能。

我们再来vwin 获取动态分页列表的流程:

使用 ZSet 的 ZREVRANGE 命令 ,传入分页参数,查询出动态 ID 列表 ;

传递动态 ID 列表参数,通过 Redis 的 pipleline 功能从缓存中批量获取动态的详情,评论,点赞,收藏这些功能数据 ,组装成列表 。

4 总结

本文介绍了实现分页列表缓存的三种方式:

直接缓存分页列表结果

查询对象ID列表,只缓存每个对象条目

缓存对象ID列表,同时缓存每个对象条目

这三种方式是一层一层递进的,要诀是:细粒度的控制缓存批量加载对象





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网 网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 存储器
    +关注

    关注

    38

    文章

    7484

    浏览量

    163759
  • SQL
    SQL
    +关注

    关注

    1

    文章

    762

    浏览量

    44114
  • MYSQL数据库
    +关注

    关注

    0

    文章

    96

    浏览量

    9389
  • Redis
    +关注

    关注

    0

    文章

    374

    浏览量

    10871

原文标题:分页列表缓存就该这样设计!

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    多列列表分页显示

    现在希望对多列列表框做如下操作:从数据库中输入10000行数据导入多列列表框,要求多列列表框能够分100页显示,每页显示100条数据,且能够利用数值控件进行翻页。
    发表于 11-04 11:15

    mysql分页

    第7章 分页程序(4课时)
    发表于 04-30 11:51

    JPA分页查询的常用方法

    JPA分页查询与条件分页查询
    发表于 10-23 17:10

    请问mybatis分页插件有哪些使用案列?

    mybatis分页插件使用案例
    发表于 11-09 06:21

    聊聊环形缓存在单片机程序中的使用

    片头因为环形缓存在单片机程序中的使用是非常有效的,非常有用的,关于这个话题在此专门开一文章来聊聊这个话题。环形缓存的用途主要是来缓存数据,而需要缓存
    发表于 12-06 08:29

    XML数据分页索引技术研究

    对海量XML文档的索引查询技术进行研究,提出一种XML数据分页索引查询实现方法。该方法把页面元素标记数量作为数据分页依据,建立XML数据的分页索引,并在该分页索引上实现XPath
    发表于 03-31 10:07 10次下载

    Jquery简单分页实现

    这篇文章主要介绍了Jquery简单分页实现方法,实例分析了jquery分页的相关实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下。
    发表于 11-28 11:55 1078次阅读

    串口 单片机 文件_从环形缓存到流水缓存在STM32单片机的应用

    片头因为环形缓存在单片机程序中的使用是非常有效的,非常有用的,关于这个话题在此专门开一文章来聊聊这个话题。环形缓存的用途主要是来缓存数据,而需要缓存
    发表于 11-23 18:21 15次下载
    串口 单片机  文件_从环形<b class='flag-5'>缓存</b>到流水<b class='flag-5'>缓存</b>在STM32单片机的应用

    如何优化MySQL百万数据的深分页问题

    我们日常做分页需求时,一般会用limit实现,但是当偏移量特别大的时候,查询效率就变得低下。本文将分四个方案,讨论如何优化MySQL百万数据的深分页问题,并附上最近优化生产慢SQL的实战案例。
    的头像 发表于 04-06 15:12 1890次阅读

    聊聊缓存数据库一致性

    在云服务中,缓存是极其重要的一点。所谓缓存,其实是一个高速数据存储层。当缓存存在后,日后再次请求该数据就会直接访问缓存,提升数据访问的速度。
    的头像 发表于 01-30 17:41 768次阅读

    图文详解Linux分页机制

    分页机制是 80x86 内存管理机制的第二种机制,分段机制用于把虚拟地址转换为线性地址,而分页机制用于把线性地址转换为物理地址。
    发表于 05-30 09:10 449次阅读
    图文详解Linux<b class='flag-5'>分页</b>机制

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

    本地缓存 :应用中的缓存组件,缓存组件和应用在同一进程中,缓存的读写非常快,没有网络开销。但各应用或集群的各节点都需要维护自己的单独缓存,无
    发表于 06-11 15:12 825次阅读
    <b class='flag-5'>聊聊</b>本地<b class='flag-5'>缓存</b>和分布式<b class='flag-5'>缓存</b>

    聊聊如何实现一种闪存缓存设计

    许多web服务需要对数十亿个小对象实现快速访问,而每个小对象只有几百个字节。为了实现这一点同时考虑实际生产效益,缓存系统必须做到同时低成本,大容量与高性能。
    的头像 发表于 08-29 09:01 714次阅读
    <b class='flag-5'>聊聊</b>如何实现一种闪存<b class='flag-5'>缓存</b>设计

    mybatis逻辑分页和物理分页的区别

    MyBatis是一个开源的Java持久层框架,它与其他ORM(对象关系映射)框架相比,具有更加灵活和高性能的特点。MyBatis提供了两种分页方式,即逻辑分页和物理分页。在本文中,我们将详细介绍
    的头像 发表于 12-03 14:54 897次阅读

    聊聊缓存击穿的解决方法

    缓存击穿,Redis中的某个热点key不存在或者过期,但是此时有大量的用户访问该key。比如xxx直播间优惠券抢购、xxx商品活动,这时候大量用户会在某个时间点一同访问该热点事件。但是可能
    的头像 发表于 10-23 13:54 165次阅读