长列表分页缓存策略

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

 

 

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

顾名思义,就是直接缓存每次分页查询的结果。

 

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比较合适,特别对直播流有较强的人工运营需求(比如往流中插入一些竞猜题目或用户的评论).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值