高性能优惠叠加计算框架 Q-calculator 发布!
https://github.com/CyrilFeng/Q-calculator
优惠是营销转化链路的重要抓手,对刺激用户消费起到至关重要的作用,目前市场上的优惠主要包含活动(如拼多多的砍一刀、天猫农场、新用户首购、复购、积分等)和券(如折扣券、代金券、商品券、买A赠B等),复杂的优惠规则让用户很难自行计算优惠叠加的顺序,或许用户在复杂的优惠规则中降低购买商品的欲望,对于参加了多个活动、有多个优惠券情况尤为明显。
优惠的计算顺序可以分为平行式和渐进式,其中平行式优惠之间没有依赖关系,而渐进式优惠之间则存在依赖关系,即下一个优惠取决于上一步的优惠结果,假设小明消费了100元,有一个8折优惠券和一个满100-20的优惠券,则这2个优惠的使用顺序有以下两种情况:
Q-calculator
采用很多新颖的设计实现了高性能求解优惠最优排列。
核心计算类 Permutation
Permutation
是一个抽象类,是Q-calculator
的核心,在Permutation
中使用了很多优化策略来保证性能,这些策略包括:
- 预存的排列数结果集
这么设计的原因是在业务场景中需要频繁的计算排列,对于某个长度的序列,其排列结果是固定的。在Permutation
类中的PERMUTATIONS
属性存放了7以内的排列数结果集,这里使用了Byte
来存储,因此占用的内存空间非常小。
private final static Map<Integer,Collection<List<Byte>>> PERMUTATIONS = Maps.newHashMap();
这个动作在类加载即完成,如果对7不满意,可以调整SUPPORTEDSIZE
的大小,7是我们在实现中摸出来的兼顾业务和性能的参数,大家可以根据自己的需要来调整。
- \(A_n^3\) 级别缓存
相对于传统的Key-Value
结构,求解 \(A_n^n\) 问题的缓存需要特殊设计,对一个优惠集合而言 \(A_n^3\) 意味着缓存 n x (n-1) x (n-2) 条数据,默认n为7则需要缓存210条数据,兼顾内存大小和缓存带来的性能收益, \(A_n^3\) 是最为合适的。
Permutation
的成员变量cache
来实现高性能缓存。
private final Map<Integer, CalcState<T>> cache = Maps.newHashMap();
可能你已经注意到,cache
的键是Integer
类型的,的确,通常String
会更常用,然而在万次计算的场景下,String
的拼接已经成了瓶颈。 为了实现高性能的键,Permutation
通过位移对Byte
数组的前3位进行扰动,确保键的唯一性和性能。
private static Integer calcKey(List<Byte> a){ return a.size()>=3?(a.get(0) << 6)+ (a.get(1) << 3) + a.get(2):0; }
Permutation
提供了保存点来实现 \(A_n^3\) 级别缓存,CalcState
记录了计算到第3步的状态,包括当前订单优惠金额和计算过程、已享用优惠的商品等,这些属性的保存和回放Permutation
已经帮你做好了,Permutation
额外提供了抽象的保存和回放方法来满足你的个性化诉求。
优惠计算是有优先级的,必须保证属性calculateGroup
值小的在前面运算,当backToSnapshot
发生时,需要额外判断缓存中最后一个优惠和当前准备计算优惠之间的关系,若不满足则直接跳出。checkIfWakeUpJump
方法将在缓存被使用后立刻判断是否需要继续下去。
上下文类 DiscountContext
DiscountContext
是上下文,也是Permutation
的成员变量,DiscountContext
同样包含很多优化策略:
- CalcStage数组
在变更最频繁也是最重要的计算步骤对象CalcStage
使用数组存储,该数组随着上下文创建而创建,在Permutation
中使用
Arrays.fill(arr,null);
将该数组清空并让它投入下一次计算,这样一次全排列过程中,数组只会被创建一次,避免了频繁创建数组带来的性能损耗。
- 预计算
DiscountContext
的初始化方法是静态的create
方法,该方法将商品和优惠进行绑定,同时执行一些用户自定义的逻辑,我们称之为预计算
,预计算的结果会被保存在DiscountContext
的preCompute
属性,可以在后续的计算中直接取用,一劳永逸,避免了在后续的高速迭代中做相同的事情,比如商品分组、求和等等。
预计算 PreCompute
预计算提供了接口,要使用预计算首先需要实现PreCompute接口
public interface PreCompute<T extends GoodsItem> { /** * 判断符合条件的活动类型,符合才会执行preComputeItems */ Set<String> matchTypes(); /** * 对商品做一些复杂集合操作 * @param items 当前参与优惠的商品 * @param discount 当前优惠 * @param preCompute 存储计算的结果 */ void preComputeItems(List<T> items, DiscountWrapper discount, Map<String,Object> preCompute); }
计算器 Calculator
Calculator
是单个优惠的计算接口,它有calcWarp
一个方法,负责具体的优惠计算,但calcWarp
需要承担一些内部的事情,因此我们提供了抽象类AbstractCalculator
实现了calcWarp
,并最终暴露了一个更简单的calc
方法给使用者。最终用户继承AbstractCalculator
,需要在Component
注解中指定一个值,而CalculatorRouter
将通过这个值来路由到具体的优惠计算器。这个值和DiscountWrapper
中的type
属性是对应的。
@Component("manjian") public class ManjianCalc extends AbstractCalculator<GoodsItem> { ...... }
用户列表(部分):

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
如何平衡商业化与开源的冲突
开源商业化是久经不衰的话题,在《中国开源社区 Landscape 社区畅聊》系列直播的第四期,我们邀请了 Zilliz 社区开发者关系 & 市场运营负责人李晨、Juicedata 合伙人 & JuiceFS 开源社区 1 号成员苏锐、云智慧企业效能高级总监 & AIOps 云智慧社区 PMC 高驰涛,畅聊探讨“如何平衡商业化与开源的冲突”这个话题,本次直播的内容包括但不限于商业公司如何定制开源战略,如何让开源融入到公司文化中、如何平衡商业盈利和开源社区的需求、 开源商业化的踩坑经验等话题 直播期间可随时提问,嘉宾会抽取高质量问题进行解答,被选中的提问者可以获得礼品一份。 一、直播主题 主题:如何平衡商业化与开源的冲突 时间:11 月 3 日 20:00 ~ 21:30 平台:“OSC 开源社区” 视频号 主办方:中国开源社区 Landscape 社区& JuiceFS 社区 & AIOPS 云智慧社区 二、直播嘉宾 主持人:李晨 Zilliz 开发者关系及市场运营负责人,Linux Foundation APAC Evangelist,LF ...
- 下一篇
干货收藏|Clickhouse 常见问题及解决方案汇总
常见问题 偶尔出现 CLOSE_WAIT 情况 CLOSE_WAIT 占用的是网络端口资源,一台机器可以有6万多个端口,如果偶尔有 CLOSE_WAIT 的情况,也不用太着急 ,只要 CLOSE_WAIT 不是迅速持续地增加,一般来说该情况也会在数小时后被系统回收掉。 频繁出现 CLOSE_WAIT 情况 如果系统有大量CLOSE_WAIT,主要表现是在有句柄操作时会报"too many open files" 的问题,这时候服务不可访问,甚至 SSH 都连不上。 但"too many open files" 的问题也有可能是打开的文件句柄太多导致,即 ClickHouse 写入太频繁或者查询的时候经常要查大量的历史数据。 出现"too many parts" 情况 "too many parts" 一般是 ClickHouse 写入太频繁,导致 Parts 没有及时合并引起的,也有可能有大查询导致磁盘 IO 被占用而受影响。 出现"too many simultaneous queries" 情况 "too many simultaneous queries" 表面上来看是查询并发引...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL8.0.19开启GTID主从同步CentOS8
- Mario游戏-低调大师作品
- CentOS7安装Docker,走上虚拟化容器引擎之路