分页列表缓存,你真的会吗 当前独家
开源中国的红薯哥写了很多关于缓存的文章,其中多级缓存思路,分页列表缓存这些知识点给了我很大的启发性。
写这篇文章,我们聊聊分页列表缓存,希望能帮助大家提升缓存技术认知。
(相关资料图)
1 直接缓存分页列表结果显而易见,这是最简单易懂的方式。
我们按照不同的分页条件来缓存分页结果 ,伪代码如下:
public List getPageList(String param,int page,int size) { String key = "productList:page:" + page + ”size:“ + size + "param:" + param ; List dataList = cacheUtils.get(key); if(dataList != null) { return dataList; } dataList = queryFromDataBase(param,page,size); if(dataList != null) { cacheUtils.set(key , dataList , Constants.ExpireTime); }}
这种方案的优点是工程简单,性能也快,但是有一个非常明显的缺陷基因:列表缓存的颗粒度非常大。
假如列表中数据发生增删,为了保证数据的一致性,需要修改分页列表缓存。
有两种方式 :
1、依靠缓存过期来惰性的实现 ,但业务场景必须包容;
2、使用 Redis 的 keys 找到该业务的分页缓存,执行删除指令。 但 keys 命令对性能影响很大,会导致 Redis 很大的延迟 。
生产环境使用 keys 命令比较危险,发生事故的几率高,非常不推荐使用。
2 查询对象ID列表,再缓存每个对象条目缓存分页结果虽然好用,但缓存的颗粒度太大,保证数据一致性比较麻烦。
所以我们的目标是更细粒度的控制缓存。
我们查询出商品分页对象ID列表,然后为每一个商品对象创建缓存 , 通过商品ID和商品对象缓存聚合成列表返回给前端。
伪代码如下:
核心流程:
1、从数据库中查询分页 ID 列表
// 从数据库中查询分页商品 ID 列表List productIdList = queryProductIdListFromDabaBase( param, page, size);
对应的 SQL 类似:
SELECT id FROM productsORDER BY id LIMIT (page - 1) * size , size
2、批量从缓存中获取商品对象
Map cachedProductMap = cacheUtils.mget(productIdList);
假如我们使用本地缓存,直接一条一条从本地缓存中聚合也极快。
假如我们使用分布式缓存,Redis 天然支持批量查询的命令 ,比如 mget ,hmget 。
3、组装没有命中的商品ID
List noHitIdList = new ArrayList<>(cachedProductMap.size());for (Long productId : productIdList) { if (!cachedProductMap.containsKey(productId)) { noHitIdList.add(productId); }}
因为缓存中可能因为过期或者其他原因导致缓存没有命中的情况,所以我们需要找到哪些商品没有在缓存里。
4、批量从数据库查询未命中的商品信息列表,重新加载到缓存
首先从数据库里批量查询出未命中的商品信息列表 ,请注意是批量。
List noHitProductList = batchQuery(noHitIdList);
参数是未命中缓存的商品ID列表,组装成对应的 SQL,这样性能更快 :
SELECT * FROM products WHERE id IN (1, 2, 3, 4);
然后这些未命中的商品信息存储到缓存里 , 使用 Redis 的 mset 命令。
//将没有命中的商品加入到缓存里Map noHitProductMap = noHitProductList.stream() .collect( Collectors.toMap(Product::getId, Function.identity()) );cacheUtils.mset(noHitProductMap);//将没有命中的商品加入到聚合map里cachedProductMap.putAll(noHitProductMap);
5、 遍历商品ID列表,组装对象列表
for (Long productId : productIdList) { Product product = cachedProductMap.get(productId); if (product != null) { result.add(product); }}
当前方案里,缓存都有命中的情况下,经过两次网络 IO ,第一次数据库查询 IO ,第二次 Redis 查询 IO , 性能都会比较好。
所有的操作都是批量操作,就算有缓存没有命中的情况,整体速度也较快。
”查询对象ID列表,再缓存每个对象条目“ 这个方案比较灵活,当我们查询对象ID列表,可以不限于数据库,还可以是搜索引擎,Redis 等等。
下图是开源中国的搜索流程:
精髓在于:搜索的分页结果只包含业务对象 ID ,对象的详细资料需要从缓存 + MySQL 中获取。
3 缓存对象ID列表,同时缓存每个对象条目笔者曾经重构过类似朋友圈的服务,进入班级页面 ,瀑布流的形式展示班级成员的所有动态。
我们使用推模式将每一条动态 ID 存储在 Redis ZSet 数据结构中 。Redis ZSet 是一种类型为有序集合的数据结构,它由多个有序的唯一的字符串元素组成,每个元素都关联着一个浮点数分值。
ZSet 使用的是 member -> score 结构 :
member : 被排序的标识,也是默认的第二排序维度( score 相同时,Redis 以 member 的字典序排列)score : 被排序的分值,存储类型是 double如上图所示:ZSet 存储动态 ID 列表 , member 的值是动态编号 , score 值是创建时间。
通过 ZSet 的 ZREVRANGE 命令就可以实现分页的效果。
ZREVRANGE 是 Redis 中用于有序集合(sorted set)的命令之一,它用于按照成员的分数从大到小返回有序集合中的指定范围的成员。
为了达到分页的效果,传递如下的分页参数 :
通过 ZREVRANGE 命令,我们可以查询出动态 ID 列表。
查询出动态 ID 列表后,还需要缓存每个动态对象条目,动态对象包含了详情,评论,点赞,收藏这些功能数据 ,我们需要为这些数据提供单独做缓存配置。
无论是查询缓存,还是重新写入缓存,为了提升系统性能,批量操作效率更高。
若缓存对象结构简单,使用 mget 、hmget 命令;若结构复杂,可以考虑使用 pipleline,Lua 脚本模式 。笔者选择的批量方案是 Redis 的 pipleline 功能。
我们再来模拟获取动态分页列表的流程:
使用 ZSet 的 ZREVRANGE 命令 ,传入分页参数,查询出动态 ID 列表 ;传递动态 ID 列表参数,通过 Redis 的 pipleline 功能从缓存中批量获取动态的详情,评论,点赞,收藏这些功能数据 ,组装成列表 。4 总结本文介绍了实现分页列表缓存的三种方式:
直接缓存分页列表结果
查询对象ID列表,只缓存每个对象条目
缓存对象ID列表,同时缓存每个对象条目
这三种方式是一层一层递进的,要诀是:
细粒度的控制缓存和批量加载对象。
如果我的文章对你有所帮助,还请帮忙点赞、在看、转发一下,你的支持会激励我输出更高质量的文章,非常感谢!
关键词:
-
分页列表缓存,你真的会吗 当前独家
2023-05-23 -
看热讯:课堂评价多维度,东华小学让学生成长看得见
2023-05-23 -
中邮证券:短期生猪价格或继续低迷 下半年将季节性回升
2023-05-23 -
判断春联时上下联的简要方法_怎么判断春联时上下联 当前快播
2023-05-23 -
【东海期货5月23日产业链日报】贵金属篇:美联储官员发言分歧,金银偏强 环球动态
2023-05-23 -
观察:山西汾酒、中际旭创等13股获融资净买入超5000万元
2023-05-23 -
【新要闻】满帮第一季度营收17亿元 净利润5.1亿元
2023-05-23 -
22年华裔女博士,因3万物业费和美国警察对峙,最终被乱枪射杀!|世界百事通
2023-05-23 -
沪指跌0.72% 通信行业跌幅最大 全球热点
2023-05-23 -
中信证券:挖掘需求侧资源,支持新型电力系统发展|世界速递
2023-05-23 -
贵州赫章:开展第33个“全国助残日”服务下基层活动
2023-05-23 -
公主日记2在线观看免费
2023-05-23 -
苹果手机怎么导入铃声多多歌曲做铃声 苹果手机怎么导入铃声
2023-05-23 -
推荐温戈的《了不起的芯片》一书
2023-05-23 -
天天滚动:如何让冰箱更加保鲜呢 如何让冰箱更加保鲜呢
2023-05-23 -
洛杉矶机场爆炸-世界热资讯
2023-05-23 -
生命由原子组成,既然原子可以永生,为什么生命不行?
2023-05-23 -
全球热讯:南通通州兴东街道:绿色新家园,送到“百姓家门口”
2023-05-23 -
江苏扬州:千名大学生“后浪”科技创客相聚“青年科创节”|天天快消息
2023-05-22 -
2023青春节粮禾下有梦团课直播入口+回放入口
2023-05-22 -
厚朴的功效与作用是什么_厚朴的功效与作用
2023-05-22 -
再见RNG?LPL官方大名单更新:RNG只剩3人!Gala和ming已不在队内|环球快资讯
2023-05-22 -
当前资讯!不罢休是什么生肖_呶呶不休打一生肖
2023-05-22 -
金新城揭阳新设万豪物业,注册资本50万元
2023-05-22 -
天邦食品:2023年预计全年屠宰量约120万头,产能利用率约24%
2023-05-22 -
环球关注:万物皆可爱!看生物王国里的欢乐派对
2023-05-22 -
龙虎榜 | 金科股份今日跌停,“拉萨天团”抱团出现
2023-05-22 -
襄阳高考专科班英语专业课程设置
2023-05-22 -
2023年5月22日周大福黄金583元/克 铂金425元/克
2023-05-22 -
世界上流量最大的河是哪条河? 世界上流量最大的河是哪条河_全球快看
2023-05-22
-
守住网络直播的伦理底线
2021-12-16 -
石窟寺文化需要基于保护的“新开发”
2021-12-16 -
电影工作者不能远离生活
2021-12-16 -
提升隧道安全管控能力 智慧高速让司乘安心
2021-12-16 -
人民财评:提升消费体验,服务同样重要
2021-12-16 -
卫冕?突破?旗手?——武大靖留给北京冬奥会三大悬念
2021-12-16 -
新能源车险专属条款出台“三电”系统、起火燃烧等都可保
2021-12-16 -
美术作品中的党史 | 第97集《窗外》
2021-12-16 -
基金销售业务违规!浦发银行厦门分行等被厦门证监局责令改正
2021-12-16 -
保持稳定发展有支撑——从11月“成绩单”看中国经济走势
2021-12-16