电商网站经常打折促销,什么双十一、双十二、六幺八、八幺八的。最常见最直接的就是对商品打折,比如A商品9.8折、B商品8折、C商品5折。而且有的商品打折还有库存限制,比如C商品打5折,但只限前50名。一般电商网站每做一次促销对系统来说都是一次考验,系统的性能和承压能力直接决定了用户体验。使用缓存能够提高网站的响应速度,我们来分析下打折促销这个业务功能的缓存一般是如何设计的。这里使用redis缓存为例,简单描述一种互联网电商系统架构中的打折促销模块缓存设计方案。
一般促销规则和产品信息是分离的,我们用促销ID标识一条促销规则,用产品ID标识一个产品(SKU)。一个产品可能会享受多个促销规则(折上折或一个产品在不同时段有不同的促销规则),同样一个促销规则可能会适用于多个产品。那么缓存的设计就要考虑以下方面:
产品ID、促销ID映射缓存
为了节省存储空间,我们的促销规则详细信息缓存和产品详细信息缓存通常是独立的。为了减少数据冗余和维护成本,我们通常会缓存产品ID与促销ID之间的映射关系。使用redis的SortedSet存储是一种比较不错的方式,score值为促销规则的预订开始时间,key格式为:
PROMOTION_MAPPING_PRODUCTANDPROMOTION_{产品ID}
value就是促销ID。由于redis并未针对有序集合的元素单独设置缓存超时时间,我们可以增加一个缓存管理器按照业务策略清除过期的缓存数据,目的是为了节省存储空间。通常过了预订结束时间的促销ID就会被移除了。
促销ID、产品ID映射缓存
有了上文的产品ID、促销ID映射缓存,为什么还要有促销ID、产品ID映射缓存呢?有时候我们做活动可能有独立的活动落地页,这个时候通常是根据活动规则(促销ID)查询有什么产品参与促销。使用redis的Set或SortedSet存储都是不错的选择,key格式为:
PROMOTION_MAPPING_PROMOTIONANDPRODUCT_{促销ID}
value就是产品ID,批量操作使用。对于未开始或进行中的促销,默认过期时间为预订结束时间,对于过了预订结束时间的促销,缓存的时间可自定义(如1小时,过了预定结束时间的促销一般查询频度会低很多)。
促销规则详情缓存
促销规则详情缓存使用redis的String结构存储就可以了,key格式为
PROMOTION_PRODUCTID_{产品ID}_PROMOTIONID_{促销ID}
值为促销规则详情。缓存过期时间默认为预定结束时间,过了预定结束时间的缓存最多存活比如1小时。(使用String结构是因为每个产品使用的促销不尽相同,每个促销的产品也不尽相同,批量查询频繁(尤其是产品列表),如果使用其他结构批量查询不好操作,应用组合好key后直接使用redis命令MGET进行批量查询,减少了应用和redis之间的频繁网络交互)。
产品促销库存缓存
产品促销库存缓存使用redis的Hash结构或String结构存储都可行,如果有不同维度的批量查询建议用String结构,虽然Hash结构也有批量查询命令,但适用场景不在此处,如果使用Hash存储,key格式为:
PROMOTION_INVENTORY_{促销ID}
filed是产品ID,value是库存值,操作库存使用HINCRBY命令。
如果使用String存储,key格式可以是:
PROMOTION_INVENTORY_PRODUCTID_{产品ID}_PROMOTIONID_{促销ID}
缓存过期时间由缓存管理器根据业务场景处理,目前考虑预定结束时间后清除。
促销缓存管理器
缓存管理器主要负责缓存的预加载、缓存重置、缓存的过期等维护任务;由于redis的缓存过期时间通常是针对key设置,对于Set、SortedSet、Hash等元素的过期没有支持,我们根据业务场景会定时对这些缓存做一些监控清理,避免存储空间越来越大。一种方案是加入缓存的时候根据业务需求得到一个缓存过期时间,把KEY或FILED加入到一个redis的有序集合,有序集合按照过期时间排序,缓存管理器只负责监控这个有序集合中需要做过期处理的KEY或FILED,进行DELETE操作即可。
拓展一下
上文提到的都是针对redis原生的一些操作设置缓存,大家都会使用一些开源的中间组件来操作缓存如spring cache、jedis、redisson等,其中redisson是一个不错的操作redis缓存的开源组件,下面是相关文档,有兴趣可以了解。
https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95
redisson拓展了一些redis的功能,主要是客户端上的一些整合和处理,是一个不错的开源组件,在分布式应用系统中可以尝试使用。前文springboot使用redisson作为分布式锁的一种实现方式就是一个不错的应用场景。