背景
在开发CNSC时,业务要求,在子账号登陆该站点时,显示的菜单全集=shop菜单并集+merchant菜单。即
业务诉求
1.首页菜单作为站点里最重要的基础功能之一,必须满足可用性的要求,且响应时间不能超过3s,否则会严重影响用户的体验。
2.用户需要尽可能快(最开始是希望能实时)地感知到到菜单的最新状态变更,假如添加了一个菜单,或者某个店铺的权限发生了变化等。
现状及存在问题
1.cnsc的每一用户拥有的店铺数差别较大,少的可能只有几个,最多拥有五千个店铺。
2.菜单逻辑本身逻辑较为复杂,每一菜单(子菜单)都可能设置了对应的feature toggle 和auth code。每个店铺的toggle属性和auth code也都不一样。菜单本身也会变动。
3.由于牵连的关联方较多,任意关联方的变动,都会导致菜单本身的逻辑发生改变。有一些变更我们也无法感知到。
4.在不做并发获取相关资源的前提下,菜单计算时间和店铺数成正比,超过一千个店铺时,单一接口返回时间都超过3s了。
5.由于CNSC用户初版无法统计出热点用户和用户习惯。所以针对所有用户菜单做缓存,缓存数据的离散性会很高,命中率会比较底,一旦没命中缓存,菜单返回时间就会大大增长。
解决思路
-
~~缓存所有菜单,最开始的时候就拿到所有的用户全集,同时对所有用户的菜单进行计算并缓存,后续定期有定时任务去不断刷新用户缓存。 ~~
否定原因:1.暂时无法获取到用户全集,且如果用户量较大,定时任务计算频率不好制定,担心一轮还没计算完成就开始要开始新一轮的计算了。
-
缓存所有菜单,最开始预热部分活跃用户,同时感知到所有可能导致用户变更的变化,由这些变更的内容出发异步任务去计算菜单更新。否定原因:1.没办法感知到所有的变化,异步任务如果出错,同样会出现接口返回缓慢,甚至一直更新不了用户菜单的情况。 。。。 。。。
-
实测得出不同店铺数的用户的菜单计算时间,基于不同的响应时间设计不同的缓存方案。在有缓存和无缓存的情况下的实际逻辑也分情况处理。
选取原因:在技术和业务上有一个较好的平衡,不会浪费太多缓存空间,方案可行性强、相对较为稳妥健壮。对于绝大部分用户来说体验也不会太差。
否定和提出新的方案的过程,也是不断和业务battle的过程。
实际设计
1.对应接口缓存和批量查询接口的实现。
2.全量菜单缓存,其它的数据需要的时候再计算出来。
3.根据不同店铺数,设计不同的方案。
4.有任意进程更新缓存时,其它进程就不需要再浪费资源一起做更新了。(通过标识位+5min的计算时间同时决定)
5.计算后的菜单进行缓存,物理过期时间为30天,逻辑过期时间为30min,强制缓存更新计算时间为35min。
总结
在实际的场景下,特别有时候在复杂业务的场景下,并没有一种普适的缓存策略或通用方案可以完美符合相关需求的。更多的时候,还是要根据具体的场景分析来决定具体使用的方案,有的时候,甚至会混合几种方案一起使用,根据不同情况应用对应方案的不同细节设计。
例如上述的cnsc 菜单缓存方案,其实也并没有完全应用cache aside的方案,同时也用到了write back方案中的异步更新的思路。
每个方案都有每个方案的优点和缺点,根据业务需求的属性,具体分析并能找出合适的方案,才是最重要的。
其它场景
Gateway的路由数据:缓存穿透、缓存雪崩。
设计:更新redis缓存加锁。本地缓存随机过期。多级缓存。
最新的方案
不再由请求触发local cache,是有一个异步的定时任务去触发更新?更像是一个write back的缓存策略?