总结两种常见的长列表分页缓存策略

简介: 通常,对于长列表加载的场景,都需要进行分页, 如最近的世界杯体育垂站项目中的赛程页,评论流,直播流。而为了提高分页加载的性能,往往需要对分页进行缓存。 下面总结对两种常见的分页缓存的策略, 适用场景以及各自的优缺点。     策略一: 直接对分页结果缓存 顾名思义,就是直接缓存每次分页查询的结果。  
通常,对于长列表加载的场景,都需要进行分页, 如最近的世界杯体育垂站项目中的赛程页,评论流,直播流。而为了提高分页加载的性能,往往需要对分页进行缓存。 下面总结对两种常见的分页缓存的策略, 适用场景以及各自的优缺点。
 
 

策略一: 直接对分页结果缓存

顾名思义,就是直接缓存每次分页查询的结果。
 
1531379878187-e71c1767-b892-4b26-9751-c7
 
适用场景
  • 列表长度无法提前预知
  • 数据和排序不经常变更
 
优点:
  • 实现简单
  • 通常能获得不错的性能。由于直接缓存分页的结果,因此只需一次IO就能把数据查出来
 
缺点
  • 无法及时更新
随着列表增长,一个对象可能从第1页转移到第2页。因此当某个对象发生变更(或排序权重发生变化)后,无法判断该更新哪几页的缓存;除非同步维护一张倒排表,记录每个对象ID到所有缓存键值的映射关系, 但对于一般web应用实现成本有点高
 
  • 数据不一致
由于无法做到实时主动更新,因此失效时间不宜设置过长,这就需要根据实际业务场景(比如允许一定的更新延迟) 选取一个能接受的值, 而在缓存失效之前,需要忍受数据不一致。
 
  • 缓存键值(cacheKey)设计或使用不当, 可能会产生大量无效的缓存垃圾
假设分页查询的条件是 _prev_pos 和 size,_prev_pos为上一页最后一个对象的_pos值, 即从某个对象开始向前(或向后)检索size个对象, 则cacheKey = (_prev_pos+size), 选择不同的_prev_pos 和 size 会生成 不同的cacheKey。
类似的还有cacheKey= md5(url) , cacheKey = (startTime + endTime), 使用不当时也会产生大量垃圾cacheKey.
产生大量垃圾cacheKey的直接后果是,缓存空间会很快被耗尽,频繁触发LRU,最终影响应用的性能
 
 
 

策略二:缓存整个对象ID列表,再分别对每个对象建立缓存

 
1531381115475-5956c61a-bf56-4c0d-a62e-fa
预先把整个对象ID列表缓存起来,由程序实现分页逻辑,分页查询的结果是一个ID数组, 然后再根据每个ID从缓存中查找对象,最后组装成一个对象数组返回
 
适用场景
  • 列表长度有限
  • 对象数据或排序权重需要频繁更新
 
优点
  • 数据一致
在这种存储结构下, 当对象数据和排序权重发生变更时,能及时更新对应的缓存块,避免出现缓存的数据和数据库不一致的情况。又由于每次分页查询都是一次动态计算的结果,因此只要缓存更新了,就一定能拿到最新的结果
 
  • 缓存空间的大小是恒定且能提前预估
 
  • 缓存块能设置比较长的过期时间,不用担心缓存失效
 
缺点
  • IO次数 = n + 1 ( n 为每页的条数),为了保证性能, n 通常不能选得过大
  • 列表长度和 分页逻辑的算法 直接影响查询性能
  • 实现成本略高
 
 

策略3:缓存分页结果,并定时更新前几页缓存

在策略1的基础上,增加一个定时任务,定时刷新前几页的缓存, 从而尽量保证前几页的缓存是最新的。
 
由于在某些业务场景下,用户只会浏览前几页的内容,比如用户一般只会关注未来 1 ~ 2周的体育赛事, 因此只要保证前几页的内容是最新的即可。
 
 

总结

在项目实践中,我发现没有一种缓存策略能完美解决所有问题,往往需要在 性能 和 数据一致性之间寻找一个平衡点。 比如对于体育赛程列表,由于更新频率不高的特点,适合采用策略1 对赛程列表进行分页缓存,但是对于比赛直播流, 则采用策略2或策略3比较合适,特别对直播流有较强的人工运营需求(比如往流中插入一些竞猜题目或用户的评论).
 

 

目录
相关文章
|
30天前
|
缓存 Java
Java本地高性能缓存实践问题之Caffeine缓存库中基于时间设置驱逐策略的问题如何解决
Java本地高性能缓存实践问题之Caffeine缓存库中基于时间设置驱逐策略的问题如何解决
|
7天前
|
缓存 JavaScript 中间件
优化Express.js应用程序性能:缓存策略、请求压缩和路由匹配
在开发Express.js应用时,采用合理的缓存策略、请求压缩及优化路由匹配可大幅提升性能。本文介绍如何利用`express.static`实现缓存、`compression`中间件压缩响应数据,并通过精确匹配、模块化路由及参数化路由提高路由处理效率,从而打造高效应用。
29 5
|
17天前
|
缓存 NoSQL Java
揭秘性能提升的超级武器:掌握Hibernate二级缓存策略!
【9月更文挑战第3天】在软件开发中,性能优化至关重要。使用Hibernate进行数据持久化的应用可通过二级缓存提升数据访问速度。一级缓存随Session生命周期变化,而二级缓存是SessionFactory级别的全局缓存,能显著减少数据库访问次数,提高性能。要启用二级缓存,需在映射文件或实体类上添加相应配置。然而,并非所有场景都适合使用二级缓存,需根据业务需求和数据变更频率决定。此外,还可与EhCache、Redis等第三方缓存集成,进一步增强缓存效果。合理运用二级缓存策略,有助于大幅提升应用性能。
35 5
|
17天前
|
存储 缓存 前端开发
缓存技术在软件开发中的应用与优化策略
缓存技术在软件开发中的应用与优化策略
|
25天前
|
存储 缓存 监控
系统设计:在搜索系统实现缓存的策略与思考
【8月更文挑战第26天】在构建高性能的搜索系统时,缓存策略是优化查询响应时间和减轻后端数据库压力的关键手段。随着数据量的激增和用户查询需求的多样化,如何设计并实现一套高效、可扩展且易于维护的缓存机制,成为了技术团队面临的重要挑战。本文将深入探讨搜索系统中缓存策略的设计思路与实践经验,旨在为读者提供一套系统性的解决方案。
37 1
|
26天前
|
缓存 算法 前端开发
深入理解缓存淘汰策略:LRU和LFU算法的解析与应用
【8月更文挑战第25天】在计算机科学领域,高效管理资源对于提升系统性能至关重要。内存缓存作为一种加速数据读取的有效方法,其管理策略直接影响整体性能。本文重点介绍两种常用的缓存淘汰算法:LRU(最近最少使用)和LFU(最不经常使用)。LRU算法依据数据最近是否被访问来进行淘汰决策;而LFU算法则根据数据的访问频率做出判断。这两种算法各有特点,适用于不同的应用场景。通过深入分析这两种算法的原理、实现方式及适用场景,本文旨在帮助开发者更好地理解缓存管理机制,从而在实际应用中作出更合理的选择,有效提升系统性能和用户体验。
56 1
|
2月前
|
canal 缓存 NoSQL
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;先删除缓存还是先修改数据库,双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
|
25天前
|
存储 缓存 NoSQL
微服务复杂查询之缓存策略
微服务复杂查询之缓存策略
|
2月前
|
缓存 Python
在Python中,`functools`模块提供了一个非常有用的装饰器`lru_cache()`,它实现了最近最少使用(Least Recently Used, LRU)缓存策略。
在Python中,`functools`模块提供了一个非常有用的装饰器`lru_cache()`,它实现了最近最少使用(Least Recently Used, LRU)缓存策略。
|
2月前
|
缓存 算法 API
深入理解后端开发中的缓存策略
【7月更文挑战第15天】缓存是提高后端系统性能和扩展性的关键机制之一。本文将深入探讨后端开发中缓存的应用,包括缓存的基本原理、类型、以及在实际应用中的策略。我们将从缓存的定义开始,逐步介绍缓存在数据库查询、API响应和分布式系统中的优化作用。通过实例分析常见的缓存模式,如LRU、LFU和FIFO,并讨论它们在不同场景下的适用性。最后,文章还将涵盖缓存一致性问题和解决方案,帮助读者构建高效且可靠的后端系统。