问题场景
- 促销系统中,针对某个商品已配置了多个促销活动,且这些活动已经处于进行中
- 此时新建了一个促销活动,状态为未开始
- 算价查询促销活动时,仅获取到了未来开始的活动,没有获取到已开始的活动,导致商品价格没有优惠
问题背景
- 促销系统为了提升算价性能,以商品sku为key,促销活动集合为value,存放于redis与guava中
- guava的过期时间为48小时,若该sku在48小时内没有发生查询,则该本地缓存将过期
- 查询策略为优先查询guava,guava中存在数据则直接返回,不存在则查询redis,再将数据存入guava
问题分析
- 当查询该sku的促销活动时,系统获取处于进行中的活动,并存放在guava中
- 48小时后,由于没有算价请求,该sku对应的本地缓存过期
- 此时对该sku新建促销活动,B端触发保存策略,将新建的活动保存至redis与guava中,但此处保存至guava实际上是増量保存,并不是通过sku进行全量活动获取,导致guava中仅存在新增的活动
- 此时算价查询该sku的促销活动,仅获取了未生效的活动,导致价格没有优惠
问题处理
调整guava的写入策略,在新建促销活动时,拉取该sku对应的全量活动,并放入guava中
问题思考
本地缓存涉及到临界问题时,需要考虑缓存写入与读取的方案是否合理,或者说需要考虑缓存一致性问题,如果一定要使用guava,则在DB数据写入后全量重新获取受影响的数据,而不能采用増量的形式
- 例如,数据写入DB前,删除redis中的数据,写入DB后加锁从DB获取全量数据存入redis(避免高并发打崩DB),然后通知所有实例删除guava缓存,则在查询时,所有请求会从redis重新读取数据,当然具体数据删除写入逻辑还需结合具体场景讨论,此处说明的重点在于受影响的数据需要全量更新
在上述案例中,算价查询业务实际应该理解为强一致场景,而guava难以解决一致性问题,在后续优化中应该考虑去掉本地缓存,统一从redis中获取数据,且采用读写锁概念,写数据时不能读取,否则在极端场景下,对于用户感知同一时刻的算价结果可能不同
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。