带过滤的Joiners执行性能过慢,求优化方案
性能优化方案:替换过滤式连接为范围连接
你的代码性能差的核心原因是使用了Joiners.filtering(),这个操作会先对所有同Agent、同Permission的WorkingDay生成笛卡尔积,再在内存中过滤日期范围,数据量较大时会导致O(n²)的时间复杂度,性能急剧下降。
优化思路
用OptaPlanner支持的范围连接条件替代过滤式连接,让框架在连接阶段就过滤掉不符合日期范围的配对,避免全量笛卡尔积的生成,大幅减少需要处理的数据量。
同时调整分组逻辑,让统计逻辑更贴合“检查连续5天工作日”的需求:
优化后的代码
private Constraint fiveConsecutiveWorkingDaysMax(ConstraintFactory constraintFactory) { return constraintFactory .from(WorkingDay.class) .filter(WorkingDay::hasPermission) // 用范围连接替代过滤式连接,直接限制日期范围 .join(WorkingDay.class, Joiners.equal(WorkingDay::hasPermission), Joiners.equal(WorkingDay::getAgent), // wd2的日期 >= wd1日期减4天 Joiners.greaterThanOrEqual((wd1, wd2) -> wd2.getDayJava(), wd1 -> wd1.getDayJava().minusDays(4)), // wd2的日期 < wd1的日期 Joiners.lessThan((wd1, wd2) -> wd2.getDayJava(), WorkingDay::getDayJava)) // 按wd1分组,统计其窗口内的wd2数量 .groupBy((wd1, wd2) -> wd1, ConstraintCollectors.countBi()) // 当窗口内数量>=4时,说明加上wd1自身就是连续5天 .filter((wd1, count) -> count >= 4) .penalizeConfigurable(FIVE_CONSECUTIVE_WORKING_DAYS_MAX); }
额外优化建议
- 确保
WorkingDay的getAgent()、getDayJava()方法返回值的哈希和比较逻辑高效,避免不必要的计算开销。 - 如果数据集很大,可以考虑为
WorkingDay添加复合索引(比如Agent + hasPermission + dayJava),OptaPlanner能利用这类索引加速连接操作。 - 原代码分组对象为
wd2,统计的是有多少个wd1的窗口包含它,和业务需求不符;优化后改为按wd1分组,统计其前4天内的工作日数量,逻辑更贴合“禁止连续5天工作”的约束。
内容的提问来源于stack exchange,提问作者Raphawel
相关产品推荐
相关产品推荐

